Compare commits
62 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
679d4e1dcd | ||
|
|
aaa6231f89 | ||
|
|
446c8cd70c | ||
|
|
11ff1d256c | ||
|
|
4acd047982 | ||
|
|
ee4b6621e7 | ||
|
|
77517b15fd | ||
|
|
19a86a6f0d | ||
|
|
a7dee1164d | ||
|
|
7512bc0fb2 | ||
|
|
d6b57d0937 | ||
|
|
1e99c4b2ec | ||
|
|
04ca3ad8f4 | ||
|
|
2b8915e35f | ||
|
|
2a0801da6b | ||
|
|
2935d78490 | ||
|
|
2404924bcb | ||
|
|
e4c4db5cf9 | ||
|
|
e1c655c570 | ||
|
|
2788da9c51 | ||
|
|
4860a70443 | ||
|
|
22d1433ab0 | ||
|
|
ef9eedf35a | ||
|
|
a962c90b30 | ||
|
|
36275f5cce | ||
|
|
60d1c87cef | ||
|
|
9223f30f08 | ||
|
|
fe87e7f60d | ||
|
|
f3e83cf020 | ||
|
|
9eee41d74a | ||
|
|
991d76b69e | ||
|
|
6f0c7da6ad | ||
|
|
c170f576b6 | ||
|
|
1446f559cc | ||
|
|
4467e68db1 | ||
|
|
cc1118b330 | ||
|
|
a1126bd42c | ||
|
|
370523d5bf | ||
|
|
96b4d2c03d | ||
|
|
c4898502bf | ||
|
|
f7a511d1b3 | ||
|
|
b66c51a6f8 | ||
|
|
a4cf38925b | ||
|
|
41059a24a7 | ||
|
|
ee4f5a5190 | ||
|
|
141e98564c | ||
|
|
72341a6de4 | ||
|
|
55ee59ac99 | ||
|
|
61525c555e | ||
|
|
dad30b0cc2 | ||
|
|
9c9e4e86e7 | ||
|
|
b88aa46e1c | ||
|
|
10fa8a471a | ||
|
|
4e875be0dd | ||
|
|
23cc940743 | ||
|
|
4b010cc416 | ||
|
|
46f39d5fbd | ||
|
|
03515151f0 | ||
|
|
8cd723d91b | ||
|
|
6f2943ed9a | ||
|
|
e509fc3f5d | ||
|
|
707d9e12cf |
@@ -120,12 +120,14 @@ endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
|
||||
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
|
||||
option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF)
|
||||
|
||||
include (DetectArch)
|
||||
|
||||
@@ -269,6 +271,13 @@ else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
endif(ENABLE_RTI)
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
find_package(GDAL 2.0.0 REQUIRED)
|
||||
if (GDAL_FOUND)
|
||||
include_directories(${GDAL_INCLUDE_DIR})
|
||||
endif(GDAL_FOUND)
|
||||
endif(ENABLE_GDAL)
|
||||
|
||||
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_function_exists(rint HAVE_RINT)
|
||||
check_function_exists(mkdtemp HAVE_MKDTEMP)
|
||||
@@ -335,8 +344,8 @@ if (NOT ${HAVE_STD_ISNAN})
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
@@ -363,8 +372,8 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CLANG)
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
@@ -381,6 +390,19 @@ if (CLANG)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_OPENMP)
|
||||
find_package(OpenMP)
|
||||
if(OPENMP_FOUND)
|
||||
message(STATUS "OpenMP: ENABLED")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
message(STATUS "OpenMP: NOT FOUND")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenMP: DISABLED")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# boost goes haywire wrt static asserts
|
||||
check_cxx_compiler_flag(-Wno-unused-local-typedefs HAS_NOWARN_UNUSED_TYPEDEFS)
|
||||
@@ -464,7 +486,8 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
${CURL_LIBRARIES}
|
||||
${GDAL_LIBRARY})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
|
||||
@@ -17,4 +17,9 @@ set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
|
||||
set(ENABLE_SIMD @ENABLE_SIMD@)
|
||||
|
||||
# Alternative terrain engine based on pagedLOD
|
||||
|
||||
set(ENABLE_GDAL @ENABLE_GDAL@)
|
||||
set(ENABLE_OPENMP @ENABLE_OPENMP@)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHMaterial.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageNode.hxx"
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageRequest.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
#include <list>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHStaticNode.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <iostream>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasObjectPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
@@ -35,7 +35,7 @@ void SHVector2_dtor(SHVector2 *v) {
|
||||
}
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v) {
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f;
|
||||
@@ -46,7 +46,7 @@ void SHVector3_dtor(SHVector3 *v) {
|
||||
}
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v) {
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
|
||||
@@ -57,7 +57,7 @@ void SHVector4_dtor(SHVector4 *v) {
|
||||
}
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r) {
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
r->vec = _mm_setzero_ps();
|
||||
#else
|
||||
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
|
||||
@@ -148,7 +148,7 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# ifdef __SSE3__
|
||||
# include <pmmintrin.h>
|
||||
inline float hsum_ps_sse(__m128 v) {
|
||||
|
||||
@@ -21,7 +21,17 @@
|
||||
#ifndef __SHVECTORS_H
|
||||
#define __SHVECTORS_H
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear/simgear_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIMD
|
||||
# ifdef __SSE__
|
||||
// # define SHIVA_USE_SIMD
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# include <xmmintrin.h>
|
||||
float hsum_ps_sse(__m128 v);
|
||||
#endif
|
||||
@@ -41,7 +51,7 @@ void SHVector2_dtor(SHVector2 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
@@ -56,7 +66,7 @@ void SHVector3_dtor(SHVector3 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
@@ -71,7 +81,7 @@ void SHVector4_dtor(SHVector4 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,w,h; };
|
||||
@@ -88,7 +98,7 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 mtx[4];
|
||||
SHfloat m[4][4];
|
||||
@@ -117,7 +127,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
|
||||
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
|
||||
#else
|
||||
@@ -126,7 +136,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SET3V(v1,v2) { v1.vec=v2.vec; }
|
||||
# define SET4V(v1,v2) { v1.vec=v2.vec; }
|
||||
#else
|
||||
@@ -147,7 +157,7 @@ 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__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
@@ -156,7 +166,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
@@ -165,7 +175,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
@@ -174,7 +184,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
@@ -183,7 +193,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define MUL2(v,f) { v.x*=f; v.y*=f; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
@@ -192,7 +202,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define DIV2(v,f) { v.x/=f; v.y/=f; }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
@@ -201,7 +211,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#endif
|
||||
|
||||
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define ABS_MASK _mm_set1_ps(-0.f)
|
||||
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
@@ -223,7 +233,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); DIV4(v,n); }
|
||||
|
||||
#define DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
#else
|
||||
@@ -237,7 +247,7 @@ 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__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
#else
|
||||
@@ -249,7 +259,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
* Macros for matrix operations
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
|
||||
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
|
||||
@@ -262,7 +272,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
|
||||
#endif
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define SETMATMAT(m1, m2) { \
|
||||
m1.mtx[0] = m2.mtx[0]; \
|
||||
m1.mtx[1] = m2.mtx[1]; \
|
||||
@@ -275,7 +285,7 @@ int i,j; \
|
||||
m1.m[i][j] = m2.m[i][j]; }
|
||||
#endif
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MULMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
|
||||
@@ -288,7 +298,7 @@ int i,j; \
|
||||
mat.m[i][j] *= s; }
|
||||
#endif
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define DIVMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
|
||||
@@ -301,7 +311,7 @@ int i,j; \
|
||||
mat.m[i][j] /= s; }
|
||||
#endif
|
||||
|
||||
#ifdef __SSE__
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# define MULMATMAT(m2, m1, mout) { \
|
||||
int i,j; \
|
||||
for (i=0;i<4;i++) { \
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
#include "CanvasImage.hxx"
|
||||
#include "CanvasMap.hxx"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasImage.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMap.hxx"
|
||||
#include "map/geo_node_pair.hxx"
|
||||
#include "map/projection.hxx"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasPath.hxx"
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
@@ -496,18 +497,31 @@ namespace canvas
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||
charcode,
|
||||
_kerningType ) );
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize,
|
||||
previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
cursor.x() += delta.x() * wr;
|
||||
cursor.y() += delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||
previous_charcode,
|
||||
_kerningType ) );
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
cursor.x() -= delta.x() * wr;
|
||||
cursor.y() -= delta.y() * hr;
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CustomEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "DeviceEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
#include "utf8.h"
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "MouseEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BoxLayout.hxx"
|
||||
#include "SpacerItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "Layout.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "LayoutItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "NasalWidget.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "SpacerItem.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -109,10 +109,13 @@
|
||||
# define SG_UNIX
|
||||
#endif
|
||||
|
||||
#if defined( __GNUC__ )
|
||||
# define DEPRECATED __attribute__ ((deprecated))
|
||||
#ifdef __GNUC__
|
||||
#define SG_DEPRECATED(func) func __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SG_DEPRECATED(func) __declspec(deprecated) func
|
||||
#else
|
||||
# define DEPRECATED
|
||||
#pragma message("WARNING: You need to implement SG_DEPRECATED for this compiler")
|
||||
#define SG_DEPRECATED(func) func
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/BufferedLogCallback.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -46,39 +46,7 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -102,6 +70,40 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
m_class = c;
|
||||
}
|
||||
|
||||
const char* LogCallback::debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -177,7 +179,7 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
class LogStreamPrivate : public SGThread
|
||||
class logstream::LogStreamPrivate : public SGThread
|
||||
{
|
||||
private:
|
||||
/**
|
||||
@@ -482,52 +484,105 @@ public:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static logstream* global_logstream = NULL;
|
||||
static LogStreamPrivate* global_privateLogstream = NULL;
|
||||
static std::unique_ptr<logstream> global_logstream;
|
||||
static SGMutex global_logStreamLock;
|
||||
|
||||
logstream::logstream()
|
||||
{
|
||||
global_privateLogstream = new LogStreamPrivate;
|
||||
global_privateLogstream->startLog();
|
||||
d.reset(new LogStreamPrivate);
|
||||
d->startLog();
|
||||
}
|
||||
|
||||
logstream::~logstream()
|
||||
{
|
||||
popup_msgs.clear();
|
||||
global_privateLogstream->stop();
|
||||
delete global_privateLogstream;
|
||||
d->stop();
|
||||
}
|
||||
|
||||
void
|
||||
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
global_privateLogstream->setLogLevels(c, p);
|
||||
d->setLogLevels(c, p);
|
||||
}
|
||||
|
||||
void logstream::setDeveloperMode(bool devMode)
|
||||
{
|
||||
global_privateLogstream->m_developerMode = devMode;
|
||||
d->m_developerMode = devMode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
global_privateLogstream->addCallback(cb);
|
||||
d->addCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
global_privateLogstream->removeCallback(cb);
|
||||
d->removeCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
global_privateLogstream->log(c, p, fileName, line, msg);
|
||||
d->log(c, p, fileName, line, msg);
|
||||
}
|
||||
|
||||
|
||||
void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, int columns)
|
||||
{
|
||||
unsigned int i, j;
|
||||
char temp[3000], temp1[3000];
|
||||
*temp = 0;
|
||||
|
||||
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
|
||||
{
|
||||
if (strlen(temp) > 500) return;
|
||||
/* print offset */
|
||||
if (i % columns == 0)
|
||||
{
|
||||
sprintf(temp1, "0x%06x: ", i);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
|
||||
/* print hex data */
|
||||
if (i < len)
|
||||
{
|
||||
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
|
||||
/* print ASCII dump */
|
||||
if (i % columns == (columns - 1))
|
||||
{
|
||||
for (j = i - (columns - 1); j <= i; j++)
|
||||
{
|
||||
if (j >= len) /* end of block, not really printing */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
|
||||
{
|
||||
char t2[2];
|
||||
t2[0] = 0xFF & ((char*)mem)[j];
|
||||
t2[1] = 0;
|
||||
strcat(temp, t2);
|
||||
}
|
||||
else /* other char */
|
||||
{
|
||||
strcat(temp, ".");
|
||||
}
|
||||
}
|
||||
log(c, p, fileName, line, temp );
|
||||
*temp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -557,31 +612,31 @@ logstream::has_popup()
|
||||
bool
|
||||
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
return global_privateLogstream->would_log(c,p);
|
||||
return d->would_log(c,p);
|
||||
}
|
||||
|
||||
sgDebugClass
|
||||
logstream::get_log_classes() const
|
||||
{
|
||||
return global_privateLogstream->m_logClass;
|
||||
return d->m_logClass;
|
||||
}
|
||||
|
||||
sgDebugPriority
|
||||
logstream::get_log_priority() const
|
||||
{
|
||||
return global_privateLogstream->m_logPriority;
|
||||
return d->m_logPriority;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::set_log_priority( sgDebugPriority p)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
|
||||
d->setLogLevels(d->m_logClass, p);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::set_log_classes( sgDebugClass c)
|
||||
{
|
||||
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
|
||||
d->setLogLevels(c, d->m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
@@ -597,54 +652,54 @@ sglog()
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
|
||||
if( !global_logstream )
|
||||
global_logstream = new logstream();
|
||||
return *global_logstream;
|
||||
global_logstream.reset(new logstream);
|
||||
return *(global_logstream.get());
|
||||
}
|
||||
|
||||
void
|
||||
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
|
||||
d->addCallback(new FileLogCallback(aPath, c, p));
|
||||
}
|
||||
|
||||
void logstream::setStartupLoggingEnabled(bool enabled)
|
||||
{
|
||||
global_privateLogstream->setStartupLoggingEnabled(enabled);
|
||||
d->setStartupLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
void logstream::requestConsole()
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
|
||||
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
|
||||
* we don't need to handle that.
|
||||
* The new handling is quite simple:
|
||||
* 1. The constructor will ensure that these streams exists. It will attach to the
|
||||
* parent command prompt if started from the command prompt, otherwise the
|
||||
* stdout/stderr will be bound to the NUL device.
|
||||
* 2. with --console a window will always appear regardless of where the process was
|
||||
* started from. Any non redirected streams will be redirected
|
||||
* 3. You cannot use --console and either redirected stream.
|
||||
*
|
||||
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
|
||||
*/
|
||||
const bool stderrAlreadyRedirected = d->m_stderr_isRedirectedAlready;
|
||||
const bool stdoutAlreadyRedirected = d->m_stdout_isRedirectedAlready;
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
|
||||
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
|
||||
* we don't need to handle that.
|
||||
* The new handling is quite simple:
|
||||
* 1. The constructor will ensure that these streams exists. It will attach to the
|
||||
* parent command prompt if started from the command prompt, otherwise the
|
||||
* stdout/stderr will be bound to the NUL device.
|
||||
* 2. with --console a window will always appear regardless of where the process was
|
||||
* started from. Any non redirected streams will be redirected
|
||||
* 3. You cannot use --console and either redirected stream.
|
||||
*
|
||||
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
|
||||
*/
|
||||
|
||||
if (!stderrAlreadyRedirected && !stdoutAlreadyRedirected) {
|
||||
FreeConsole();
|
||||
if (AllocConsole()) {
|
||||
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
|
||||
if (!stdoutAlreadyRedirected)
|
||||
freopen("conout$", "w", stdout);
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
|
||||
if (!stderrAlreadyRedirected)
|
||||
freopen("conout$", "w", stderr);
|
||||
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
@@ -657,11 +712,19 @@ void requestConsole()
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
sglog().requestConsole();
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
delete global_logstream;
|
||||
global_logstream = 0;
|
||||
global_logstream.reset();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// forward decls
|
||||
class SGPath;
|
||||
|
||||
@@ -49,6 +50,8 @@ protected:
|
||||
LogCallback(sgDebugClass c, sgDebugPriority p);
|
||||
|
||||
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
|
||||
|
||||
static const char* debugClassToString(sgDebugClass c);
|
||||
private:
|
||||
sgDebugClass m_class;
|
||||
sgDebugPriority m_priority;
|
||||
@@ -74,6 +77,14 @@ public:
|
||||
~logstream();
|
||||
|
||||
static void initGlobalLogstream();
|
||||
|
||||
/**
|
||||
* Helper force a console on platforms where it might optional, when
|
||||
* we need to show a console. This basically means Windows at the
|
||||
* moment - on other plaforms it's a no-op
|
||||
*/
|
||||
void requestConsole();
|
||||
|
||||
/**
|
||||
* Set the global log class and priority level.
|
||||
* @param c debug class
|
||||
@@ -106,6 +117,12 @@ public:
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg);
|
||||
|
||||
/**
|
||||
* output formatted hex dump of memory block
|
||||
*/
|
||||
void hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, int columns = 16);
|
||||
|
||||
|
||||
/**
|
||||
* support for the SG_POPUP logging class
|
||||
* set the content of the popup message
|
||||
@@ -152,6 +169,10 @@ private:
|
||||
logstream();
|
||||
|
||||
std::vector<std::string> popup_msgs;
|
||||
|
||||
class LogStreamPrivate;
|
||||
|
||||
std::unique_ptr<LogStreamPrivate> d;
|
||||
};
|
||||
|
||||
logstream& sglog();
|
||||
@@ -168,12 +189,14 @@ 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)
|
||||
# define SG_HEXDUMP(C,P,MEM,LEN)
|
||||
#else
|
||||
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
|
||||
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
|
||||
#endif
|
||||
|
||||
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)
|
||||
|
||||
@@ -10,13 +10,14 @@ if(ENABLE_TESTS)
|
||||
add_executable(test_metar test_metar.cxx)
|
||||
|
||||
if (SIMGEAR_SHARED)
|
||||
target_link_libraries(test_metar SimGearScene)
|
||||
target_link_libraries(test_metar SimGearScene ${GDAL_LIBRARY})
|
||||
else()
|
||||
target_link_libraries(test_metar
|
||||
SimGearScene SimGearCore
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY})
|
||||
${RT_LIBRARY}
|
||||
${GDAL_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "DNSClient.hxx"
|
||||
#include <udns.h>
|
||||
#include <time.h>
|
||||
@@ -190,11 +191,11 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
||||
r->ttl = result->dnsnaptr_ttl;
|
||||
for (int i = 0; i < result->dnsnaptr_nrr; i++) {
|
||||
if( !r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service )
|
||||
return;
|
||||
continue;
|
||||
|
||||
//TODO: case ignore and result flags may have more than one flag
|
||||
if( !r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags )
|
||||
return;
|
||||
continue;
|
||||
|
||||
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
|
||||
r->entries.push_back(naptr);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPClient.hxx"
|
||||
#include "HTTPFileRequest.hxx"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPFileRequest.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "HTTPMemoryRequest.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "HTTPRepository.hxx"
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPRepository.hxx"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "HTTPRequest.hxx"
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -4,12 +4,14 @@ set(HEADERS
|
||||
sgstream.hxx
|
||||
gzfstream.hxx
|
||||
gzcontainerfile.hxx
|
||||
zlibstream.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
sgstream.cxx
|
||||
gzfstream.cxx
|
||||
gzcontainerfile.cxx
|
||||
zlibstream.cxx
|
||||
)
|
||||
|
||||
simgear_component(IOStreams io/iostreams "${SOURCES}" "${HEADERS}")
|
||||
@@ -20,4 +22,8 @@ if(ENABLE_TESTS)
|
||||
target_link_libraries(test_streams ${TEST_LIBS})
|
||||
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
|
||||
|
||||
add_executable(test_zlibstream zlibstream_test.cxx)
|
||||
target_link_libraries(test_zlibstream ${TEST_LIBS})
|
||||
add_test(zlibstream ${EXECUTABLE_OUTPUT_PATH}/test_zlibstream)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/compiler.h>
|
||||
#include <string>
|
||||
#include <ctype.h> // isspace()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib> // for EXIT_SUCCESS
|
||||
|
||||
|
||||
@@ -6,27 +6,33 @@
|
||||
//
|
||||
// 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 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 program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// 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.
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned(), std::underlying_type
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
#include <cassert>
|
||||
|
||||
@@ -41,6 +47,11 @@
|
||||
using std::string;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
// Cast an enum value to its underlying type
|
||||
template <typename T>
|
||||
static constexpr typename std::underlying_type<T>::type enumValue(T e) {
|
||||
return static_cast<typename std::underlying_type<T>::type>(e);
|
||||
}
|
||||
|
||||
// Private utility function
|
||||
static string zlibErrorMessage(const z_stream& zstream, int errorCode)
|
||||
@@ -75,38 +86,53 @@ static string zlibErrorMessage(const z_stream& zstream, int 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()
|
||||
// Requirement: 'val' must be non-negative.
|
||||
//
|
||||
// Return:
|
||||
// - 'val' cast as BoundType if it's lower than the largest value BoundType
|
||||
// can represent;
|
||||
// - this largest value otherwise.
|
||||
template<class BoundType, class T>
|
||||
static BoundType clipCast(T val)
|
||||
{
|
||||
uLong flags = ::zlibCompileFlags();
|
||||
std::size_t res;
|
||||
typedef typename std::make_unsigned<T>::type uT;
|
||||
typedef typename std::make_unsigned<BoundType>::type uBoundType;
|
||||
assert(val >= 0); // otherwise, the comparison and cast to uT would be unsafe
|
||||
|
||||
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.");
|
||||
// Casts to avoid the signed-compare warning; they don't affect the values,
|
||||
// since both are non-negative.
|
||||
if (static_cast<uT>(val) <
|
||||
static_cast<uBoundType>( std::numeric_limits<BoundType>::max() )) {
|
||||
return static_cast<BoundType>(val);
|
||||
} else {
|
||||
return std::numeric_limits<BoundType>::max();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static const std::size_t zlibMaxChunk = ::zlibMaxChunkSize();
|
||||
// Requirement: 'size' must be non-negative.
|
||||
//
|
||||
// Return:
|
||||
// - 'size' if it is lower than or equal to std::numeric_limits<uInt>::max();
|
||||
// - std::numeric_limits<uInt>::max() cast as a T otherwise (this is always
|
||||
// possible in a lossless way, since in this case, one has
|
||||
// 0 <= std::numeric_limits<uInt>::max() < size, and 'size' is of type T).
|
||||
//
|
||||
// Note: uInt is the type of z_stream.avail_in and z_stream.avail_out, hence
|
||||
// the function name.
|
||||
template<class T>
|
||||
static T zlibChunk(T size)
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type uT;
|
||||
assert(size >= 0); // otherwise, the comparison and cast to uT would be unsafe
|
||||
|
||||
if (static_cast<uT>(size) <= std::numeric_limits<uInt>::max()) {
|
||||
return size;
|
||||
} else {
|
||||
// In this case, we are sure that T can represent
|
||||
// std::numeric_limits<uInt>::max(), thus the cast is safe.
|
||||
return static_cast<T>(std::numeric_limits<uInt>::max());
|
||||
}
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -125,13 +151,13 @@ ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(std::istream& iStream,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _iStream(iStream),
|
||||
_iStream_p(nullptr),
|
||||
_path(path),
|
||||
_inBuf(inBuf),
|
||||
_inBufSize(inBufSize),
|
||||
_outBuf(outBuf),
|
||||
_outBufSize(outBufSize),
|
||||
_putbackSize(putbackSize)
|
||||
|
||||
{
|
||||
assert(_inBufSize > 0);
|
||||
assert(_putbackSize >= 0); // guaranteed unless the type is changed...
|
||||
@@ -164,6 +190,24 @@ ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(std::istream& iStream,
|
||||
setg(_outBuf, _outBuf, _outBuf);
|
||||
}
|
||||
|
||||
ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibAbstractIStreambuf(*iStream_p, path, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Take ownership of the object. This is a way to ensure that the _iStream
|
||||
// reference stays valid as long as our instance is alive, and that the
|
||||
// corresponding std::istream object is automatically destroyed as soon as
|
||||
// our instance is itself destroyed.
|
||||
_iStream_p = std::move(iStream_p);
|
||||
}
|
||||
|
||||
ZlibAbstractIStreambuf::~ZlibAbstractIStreambuf()
|
||||
{
|
||||
if (_inBufMustBeFreed) {
|
||||
@@ -214,8 +258,8 @@ int ZlibAbstractIStreambuf::underflow()
|
||||
std::size_t
|
||||
ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
|
||||
{
|
||||
std::ptrdiff_t remainingSpaceInOutBuf = _outBuf + _outBufSize -
|
||||
reinterpret_cast<char*>(nextOutPtr);
|
||||
std::ptrdiff_t remainingSpaceInOutBuf = (
|
||||
_outBuf + _outBufSize - reinterpret_cast<char*>(nextOutPtr));
|
||||
assert(remainingSpaceInOutBuf >= 0);
|
||||
|
||||
return static_cast<std::size_t>(remainingSpaceInOutBuf);
|
||||
@@ -226,7 +270,7 @@ ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
|
||||
[[ noreturn ]] void ZlibAbstractIStreambuf::handleZ_BUF_ERROR() const
|
||||
{
|
||||
switch (operationType()) {
|
||||
case OPERATION_TYPE_DECOMPRESSION:
|
||||
case OperationType::DECOMPRESSION:
|
||||
{
|
||||
string message = (_path.isNull()) ?
|
||||
"Got Z_BUF_ERROR from zlib while decompressing a stream. The stream "
|
||||
@@ -237,15 +281,15 @@ ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
|
||||
// When _path.isNull(), sg_location(_path) is equivalent to sg_location()
|
||||
throw sg_io_exception(message, sg_location(_path));
|
||||
}
|
||||
case OPERATION_TYPE_COMPRESSION:
|
||||
case OperationType::COMPRESSION:
|
||||
throw std::logic_error(
|
||||
"Called ZlibAbstractIStreambuf::handleZ_BUF_ERROR() with "
|
||||
"operationType() == OPERATION_TYPE_DECOMPRESSION");
|
||||
"operationType() == ZlibAbstractIStreambuf::OperationType::COMPRESSION");
|
||||
default:
|
||||
throw std::logic_error(
|
||||
"Unexpected operationType() in "
|
||||
"ZlibAbstractIStreambuf::handleZ_BUF_ERROR(): " +
|
||||
std::to_string(operationType()));
|
||||
std::to_string(enumValue(operationType())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,9 +309,7 @@ char* ZlibAbstractIStreambuf::fillOutputBuffer()
|
||||
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));
|
||||
_zstream.avail_out = clipCast<uInt>(remainingSpaceInOutBuf);
|
||||
|
||||
if (_zstream.avail_in == 0 && !allInputRead) {
|
||||
// Get data from _iStream, store it in _inBuf
|
||||
@@ -327,9 +369,7 @@ bool ZlibAbstractIStreambuf::getInputData()
|
||||
|
||||
// Data already available?
|
||||
if (alreadyAvailable > 0) {
|
||||
_zstream.avail_in = static_cast<uInt>(
|
||||
std::min(static_cast<std::size_t>(alreadyAvailable),
|
||||
zlibMaxChunk));
|
||||
_zstream.avail_in = clipCast<uInt>(alreadyAvailable);
|
||||
return allInputRead;
|
||||
}
|
||||
|
||||
@@ -340,9 +380,8 @@ bool ZlibAbstractIStreambuf::getInputData()
|
||||
|
||||
// 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()
|
||||
std::streamsize nbCharsToRead = clipCast<std::streamsize>(
|
||||
_inBuf + _inBufSize - _inBufEndPtr);
|
||||
_iStream.read(_inBufEndPtr, nbCharsToRead);
|
||||
|
||||
if (_iStream.bad()) {
|
||||
@@ -361,10 +400,8 @@ bool ZlibAbstractIStreambuf::getInputData()
|
||||
|
||||
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));
|
||||
// assert(availableChars >= 0); <-- already done in clipCast<uInt>()
|
||||
_zstream.avail_in = clipCast<uInt>(availableChars);
|
||||
|
||||
if (_iStream.eof()) {
|
||||
allInputRead = true;
|
||||
@@ -384,24 +421,34 @@ bool ZlibAbstractIStreambuf::getInputData()
|
||||
// (_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();
|
||||
// Despite the somewhat misleading footnote 296 of §27.5.3 of the C++11
|
||||
// standard, one can't assume std::size_t to be at least as large as
|
||||
// std::streamsize (64 bits std::streamsize in a 32 bits Windows program).
|
||||
std::streamsize remaining = n;
|
||||
char* origGptr = gptr();
|
||||
char* writePtr = dest; // we'll need dest later -> work with a copy
|
||||
|
||||
// First, let's take data present in our internal buffer (_outBuf)
|
||||
while (remaining > 0) {
|
||||
avail = egptr() - gptr(); // number of available chars in _outBuf
|
||||
// Number of available chars in _outBuf
|
||||
std::ptrdiff_t avail = egptr() - gptr();
|
||||
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;
|
||||
// We need an int for gbump(), at least in C++11.
|
||||
int chunkSize_i = clipCast<int>(avail);
|
||||
if (chunkSize_i > remaining) {
|
||||
chunkSize_i = static_cast<int>(remaining);
|
||||
}
|
||||
assert(chunkSize_i >= 0);
|
||||
|
||||
std::copy(gptr(), gptr() + chunkSize_i, writePtr);
|
||||
gbump(chunkSize_i);
|
||||
writePtr += chunkSize_i;
|
||||
// This cast is okay because 0 <= chunkSize_i <= remaining, which is an
|
||||
// std::streamsize
|
||||
remaining -= static_cast<std::streamsize>(chunkSize_i);
|
||||
}
|
||||
|
||||
if (remaining == 0) {
|
||||
@@ -418,9 +465,10 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
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);
|
||||
std::streamsize chunkSize_s = zlibChunk(remaining);
|
||||
// chunkSize_s > 0 and does fit in a zlib uInt: that's the whole point of
|
||||
// zlibChunk.
|
||||
_zstream.avail_out = static_cast<uInt>(chunkSize_s);
|
||||
|
||||
if (_zstream.avail_in == 0 && !allInputRead) {
|
||||
allInputRead = getInputData();
|
||||
@@ -429,8 +477,9 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
// 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;
|
||||
// chunkSize_s - _zstream.avail_out is the number of chars written by zlib.
|
||||
// 0 <= _zstream.avail_out <= chunkSize_s, which is an std::streamsize.
|
||||
remaining -= chunkSize_s - static_cast<std::streamsize>(_zstream.avail_out);
|
||||
|
||||
if (retCode == Z_BUF_ERROR) {
|
||||
handleZ_BUF_ERROR(); // doesn't return
|
||||
@@ -444,12 +493,23 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Finally, copy chars to the putback area.
|
||||
std::size_t nbPutbackChars = xsgetn_preparePutbackArea(
|
||||
origGptr, dest, reinterpret_cast<char*>(_zstream.next_out));
|
||||
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
|
||||
_outBuf + _putbackSize, // the buffer for pending,
|
||||
_outBuf + _putbackSize); // available data is empty
|
||||
|
||||
assert(remaining >= 0);
|
||||
assert(n - remaining >= 0);
|
||||
// Total number of chars copied.
|
||||
return n - remaining;
|
||||
}
|
||||
|
||||
// Utility method for xsgetn(): copy some chars to the putback area
|
||||
std::size_t ZlibAbstractIStreambuf::xsgetn_preparePutbackArea(
|
||||
char* origGptr, char* dest, char* writePtr)
|
||||
{
|
||||
// There are two buffers containing characters we potentially have to copy
|
||||
// to the putback area: the one starting at _outBuf and the one starting at
|
||||
// dest. In the following diagram, ***** represents those from _outBuf,
|
||||
@@ -472,38 +532,33 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
//
|
||||
// [1] This means that the last char represented by a star is at address
|
||||
// origGptr-1.
|
||||
|
||||
// It seems std::ptrdiff_t is the signed counterpart of std::size_t,
|
||||
// therefore this should always hold (even with equality).
|
||||
static_assert(sizeof(std::size_t) >= sizeof(std::ptrdiff_t),
|
||||
"Unexpected: sizeof(std::size_t) < sizeof(std::ptrdiff_t)");
|
||||
assert(writePtr - dest >= 0);
|
||||
std::size_t inDestBuffer = static_cast<std::size_t>(writePtr - dest);
|
||||
|
||||
assert(origGptr - eback() >= 0);
|
||||
std::size_t nbPutbackChars = std::min(
|
||||
static_cast<std::size_t>(origGptr - eback()) + inDestBuffer,
|
||||
_putbackSize);
|
||||
std::size_t nbPutbackCharsToGo = nbPutbackChars;
|
||||
|
||||
// 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::size_t chunkSize = nbPutbackChars - inDestBuffer; // yes, this number
|
||||
std::copy(origGptr - chunkSize, origGptr,
|
||||
_outBuf + _putbackSize - nbPutbackChars);
|
||||
nbPutbackCharsToGo -= chunkSize;
|
||||
}
|
||||
|
||||
// Finally, copy those that are not in _outBuf
|
||||
std::copy(writePtr - nbPutbackCharsToGo, writePtr,
|
||||
_outBuf + _putbackSize - nbPutbackCharsToGo);
|
||||
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;
|
||||
return nbPutbackChars;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
@@ -527,6 +582,24 @@ ZlibCompressorIStreambuf::ZlibCompressorIStreambuf(
|
||||
zStreamInit(compressionLevel, format, memStrategy);
|
||||
}
|
||||
|
||||
ZlibCompressorIStreambuf::ZlibCompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibCompressorIStreambuf(*iStream_p, path, compressionLevel, format,
|
||||
memStrategy, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
_iStream_p = std::move(iStream_p); // take ownership of the object
|
||||
}
|
||||
|
||||
ZlibCompressorIStreambuf::~ZlibCompressorIStreambuf()
|
||||
{
|
||||
int retCode = deflateEnd(&_zstream); // deallocate the z_stream struct
|
||||
@@ -540,7 +613,7 @@ ZlibCompressorIStreambuf::~ZlibCompressorIStreambuf()
|
||||
ZlibAbstractIStreambuf::OperationType
|
||||
ZlibCompressorIStreambuf::operationType() const
|
||||
{
|
||||
return OPERATION_TYPE_COMPRESSION;
|
||||
return OperationType::COMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
|
||||
@@ -549,30 +622,30 @@ void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
|
||||
{
|
||||
int windowBits, memLevel;
|
||||
|
||||
// Intentionally not listing ZLIB_COMPRESSION_FORMAT_AUTODETECT here (it is
|
||||
// Intentionally not listing ZLibCompressionFormat::AUTODETECT here (it is
|
||||
// only for decompression!)
|
||||
switch (format) {
|
||||
case ZLIB_COMPRESSION_FORMAT_ZLIB:
|
||||
case ZLibCompressionFormat::ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_GZIP:
|
||||
case ZLibCompressionFormat::GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(format));
|
||||
std::to_string(enumValue(format)));
|
||||
}
|
||||
|
||||
switch (memStrategy) {
|
||||
case ZLIB_FAVOR_MEMORY_OVER_SPEED:
|
||||
case ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED:
|
||||
memLevel = 8;
|
||||
break;
|
||||
case ZLIB_FAVOR_SPEED_OVER_MEMORY:
|
||||
case ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY:
|
||||
memLevel = 9;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected memory strategy: " +
|
||||
std::to_string(memStrategy));
|
||||
std::to_string(enumValue(memStrategy)));
|
||||
}
|
||||
|
||||
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
|
||||
@@ -615,6 +688,21 @@ ZlibDecompressorIStreambuf::ZlibDecompressorIStreambuf(
|
||||
zStreamInit(format);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStreambuf::ZlibDecompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibDecompressorIStreambuf(*iStream_p, path, format, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
_iStream_p = std::move(iStream_p); // take ownership of the object
|
||||
}
|
||||
|
||||
ZlibDecompressorIStreambuf::~ZlibDecompressorIStreambuf()
|
||||
{
|
||||
int retCode = inflateEnd(&_zstream); // deallocate the z_stream struct
|
||||
@@ -628,7 +716,7 @@ ZlibDecompressorIStreambuf::~ZlibDecompressorIStreambuf()
|
||||
ZlibAbstractIStreambuf::OperationType
|
||||
ZlibDecompressorIStreambuf::operationType() const
|
||||
{
|
||||
return OPERATION_TYPE_DECOMPRESSION;
|
||||
return OperationType::DECOMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
|
||||
@@ -636,18 +724,18 @@ void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
|
||||
int windowBits;
|
||||
|
||||
switch (format) {
|
||||
case ZLIB_COMPRESSION_FORMAT_ZLIB:
|
||||
case ZLibCompressionFormat::ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_GZIP:
|
||||
case ZLibCompressionFormat::GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_AUTODETECT:
|
||||
case ZLibCompressionFormat::AUTODETECT:
|
||||
windowBits = 47; // 47 = 32 + 15
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(format));
|
||||
std::to_string(enumValue(format)));
|
||||
}
|
||||
|
||||
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
|
||||
@@ -684,10 +772,31 @@ ZlibCompressorIStream::ZlibCompressorIStream(std::istream& iStream,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
|
||||
inBufSize, outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibCompressorIStream::ZlibCompressorIStream(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(std::move(iStream_p), path, compressionLevel, format,
|
||||
memStrategy, inBuf, inBufSize, outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibCompressorIStream::~ZlibCompressorIStream()
|
||||
@@ -705,10 +814,29 @@ ZlibDecompressorIStream::ZlibDecompressorIStream(std::istream& iStream,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStream::ZlibDecompressorIStream(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(std::move(iStream_p), path, format, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStream::~ZlibDecompressorIStream()
|
||||
|
||||
@@ -6,25 +6,28 @@
|
||||
//
|
||||
// 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 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 program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// 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.
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef SG_ZLIBSTREAM_HXX
|
||||
#define SG_ZLIBSTREAM_HXX
|
||||
|
||||
#include <iosfwd>
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <streambuf>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <zlib.h> // struct z_stream
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
@@ -97,15 +100,15 @@
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
enum ZLibCompressionFormat {
|
||||
ZLIB_COMPRESSION_FORMAT_ZLIB = 0,
|
||||
ZLIB_COMPRESSION_FORMAT_GZIP,
|
||||
ZLIB_COMPRESSION_FORMAT_AUTODETECT
|
||||
enum class ZLibCompressionFormat {
|
||||
ZLIB = 0,
|
||||
GZIP,
|
||||
AUTODETECT
|
||||
};
|
||||
|
||||
enum ZLibMemoryStrategy {
|
||||
ZLIB_FAVOR_MEMORY_OVER_SPEED = 0,
|
||||
ZLIB_FAVOR_SPEED_OVER_MEMORY
|
||||
enum class ZLibMemoryStrategy {
|
||||
FAVOR_MEMORY_OVER_SPEED = 0,
|
||||
FAVOR_SPEED_OVER_MEMORY
|
||||
};
|
||||
|
||||
// Abstract base class for both the compressor and decompressor stream buffers.
|
||||
@@ -144,14 +147,27 @@ public:
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
// When used, the class takes ownership of the std::istream instance pointed
|
||||
// to by the first constructor argument, and keeps it alive as long as the
|
||||
// object this constructor is for is itself alive.
|
||||
explicit ZlibAbstractIStreambuf(std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
|
||||
ZlibAbstractIStreambuf(const ZlibAbstractIStreambuf&) = delete;
|
||||
ZlibAbstractIStreambuf& operator=(const ZlibAbstractIStreambuf&) = delete;
|
||||
~ZlibAbstractIStreambuf();
|
||||
virtual ~ZlibAbstractIStreambuf();
|
||||
|
||||
protected:
|
||||
enum OperationType {
|
||||
OPERATION_TYPE_COMPRESSION = 0,
|
||||
OPERATION_TYPE_DECOMPRESSION
|
||||
enum class OperationType {
|
||||
COMPRESSION = 0,
|
||||
DECOMPRESSION
|
||||
};
|
||||
|
||||
virtual OperationType operationType() const = 0;
|
||||
@@ -166,6 +182,11 @@ protected:
|
||||
|
||||
// The input stream, from which data is read before being processed by zlib
|
||||
std::istream& _iStream;
|
||||
// Pointer to the same, used when calling the constructor that takes an
|
||||
// std::unique_ptr<std::istream> as its first argument; empty
|
||||
// std::unique_ptr object otherwise.
|
||||
std::unique_ptr<std::istream> _iStream_p;
|
||||
|
||||
// Corresponding path, if any (default-constructed SGPath instance otherwise)
|
||||
const SGPath _path;
|
||||
// Structure used to communicate with zlib
|
||||
@@ -174,12 +195,15 @@ protected:
|
||||
private:
|
||||
// Callback whose role is to refill the output buffer when it's empty and
|
||||
// the “client” tries to read more.
|
||||
int underflow() override;
|
||||
virtual int underflow() override;
|
||||
// Optional override when subclassing std::streambuf. This is the most
|
||||
// efficient way of reading several characters (as soon as we've emptied the
|
||||
// output buffer, data is written by zlib directly to the destination
|
||||
// buffer).
|
||||
std::streamsize xsgetn(char* dest, std::streamsize n) override;
|
||||
virtual std::streamsize xsgetn(char* dest, std::streamsize n) override;
|
||||
// Utility method for xsgetn()
|
||||
std::size_t xsgetn_preparePutbackArea(char* origGptr, char* dest,
|
||||
char* writePtr);
|
||||
// Make sure there is data to read in the input buffer, or signal EOF.
|
||||
bool getInputData();
|
||||
// Utility method for fillOutputBuffer()
|
||||
@@ -267,32 +291,46 @@ public:
|
||||
// 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
|
||||
// format either ZLibCompressionFormat::ZLIB or
|
||||
// ZLibCompressionFormat::GZIP
|
||||
// memStrategy either ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED or
|
||||
// ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY
|
||||
explicit ZlibCompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibCompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
|
||||
ZlibCompressorIStreambuf(const ZlibCompressorIStreambuf&) = delete;
|
||||
ZlibCompressorIStreambuf& operator=(const ZlibCompressorIStreambuf&) = delete;
|
||||
~ZlibCompressorIStreambuf();
|
||||
virtual ~ZlibCompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
OperationType operationType() const override;
|
||||
virtual OperationType operationType() const override;
|
||||
// Initialize the z_stream struct used by zlib
|
||||
void zStreamInit(int compressionLevel, ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy);
|
||||
// Call zlib's deflate() function to compress data.
|
||||
int zlibProcessData() override;
|
||||
virtual int zlibProcessData() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -307,27 +345,39 @@ 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
|
||||
// format ZLibCompressionFormat::ZLIB,
|
||||
// ZLibCompressionFormat::GZIP or
|
||||
// ZLibCompressionFormat::AUTODETECT
|
||||
explicit ZlibDecompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibDecompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
ZlibDecompressorIStreambuf(const ZlibDecompressorIStreambuf&) = delete;
|
||||
ZlibDecompressorIStreambuf& operator=(const ZlibDecompressorIStreambuf&)
|
||||
= delete;
|
||||
~ZlibDecompressorIStreambuf();
|
||||
virtual ~ZlibDecompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
OperationType operationType() const override;
|
||||
virtual OperationType operationType() const override;
|
||||
void zStreamInit(ZLibCompressionFormat format);
|
||||
int zlibProcessData() override;
|
||||
virtual int zlibProcessData() override;
|
||||
};
|
||||
|
||||
// std::istream subclass for compressing data. Input data is obtained from an
|
||||
@@ -349,16 +399,30 @@ public:
|
||||
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,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibCompressorIStream(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
ZlibCompressorIStream(const ZlibCompressorIStream&) = delete;
|
||||
ZlibCompressorIStream& operator=(const ZlibCompressorIStream&) = delete;
|
||||
~ZlibCompressorIStream();
|
||||
virtual ~ZlibCompressorIStream();
|
||||
|
||||
private:
|
||||
ZlibCompressorIStreambuf _streamBuf;
|
||||
@@ -382,15 +446,27 @@ public:
|
||||
explicit ZlibDecompressorIStream(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibDecompressorIStream(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
|
||||
ZlibDecompressorIStream(const ZlibDecompressorIStream&) = delete;
|
||||
ZlibDecompressorIStream& operator=(const ZlibDecompressorIStream&) = delete;
|
||||
~ZlibDecompressorIStream();
|
||||
virtual ~ZlibDecompressorIStream();
|
||||
|
||||
private:
|
||||
ZlibDecompressorIStreambuf _streamBuf;
|
||||
|
||||
@@ -4,36 +4,39 @@
|
||||
//
|
||||
// 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 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 program is distributed in the hope that it will be useful,
|
||||
// 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 General Public License for more details.
|
||||
// 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.
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <ios> // std::basic_ios, std::streamsize...
|
||||
#include <iostream> // std::ios_base, std::cerr, etc.
|
||||
#include <sstream>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <functional> // std::bind()
|
||||
#include <cassert>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstring> // strcmp()
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using traits = std::char_traits<char>;
|
||||
#include <zlib.h> // Z_BEST_COMPRESSION
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
@@ -41,6 +44,24 @@ using traits = std::char_traits<char>;
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
|
||||
|
||||
// Safely convert a non-negative std::streamsize into an std::size_t. If
|
||||
// impossible, bail out.
|
||||
static std::size_t streamsizeToSize_t(std::streamsize n)
|
||||
{
|
||||
SG_CHECK_GE(n, 0);
|
||||
SG_CHECK_LE(static_cast<uStreamSize>(n),
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
return static_cast<std::size_t>(n);
|
||||
}
|
||||
|
||||
// In many tests below, I use very small buffer sizes. Of course, this is bad
|
||||
// for performance. The reason I do it this way is simply because it better
|
||||
// exercises the code we want to *test* here (we are more likely to find bugs
|
||||
@@ -48,8 +69,8 @@ using traits = std::char_traits<char>;
|
||||
// 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!).
|
||||
// I suggest reading test_IStreamConstructorWithSinkSemantics() below to see
|
||||
// how to use the classes efficiently.
|
||||
|
||||
static std::default_random_engine randomNumbersGenerator;
|
||||
|
||||
@@ -177,9 +198,9 @@ void test_StreambufBasicOperations()
|
||||
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);
|
||||
text_ss, SGPath(), 8, simgear::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
nullptr, compInBufSize, nullptr, compOutBufSize, compPutbackSize);
|
||||
std::stringstream compressedOutput_ss;
|
||||
compressedOutput_ss << &compSBuf;
|
||||
|
||||
@@ -187,7 +208,7 @@ void test_StreambufBasicOperations()
|
||||
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,
|
||||
compressedOutput_ss, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
|
||||
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
|
||||
|
||||
int ch = decompSBuf.sgetc();
|
||||
@@ -214,7 +235,9 @@ void test_StreambufBasicOperations()
|
||||
// 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");
|
||||
SG_CHECK_EQUAL(n, bufSize);
|
||||
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(bufSize)),
|
||||
"3456789abc");
|
||||
|
||||
ch = decompSBuf.sungetc(); // same as sputbackc(), except no value to check
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
|
||||
@@ -226,15 +249,17 @@ void test_StreambufBasicOperations()
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
|
||||
n = decompSBuf.sgetn(buf, bufSize);
|
||||
SG_VERIFY(n == bufSize && string(buf, bufSize) == "bcdefghijk");
|
||||
SG_CHECK_EQUAL(n, bufSize);
|
||||
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(bufSize)),
|
||||
"bcdefghijk");
|
||||
|
||||
ch = decompSBuf.sungetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
|
||||
|
||||
static char buf2[64];
|
||||
n = decompSBuf.sgetn(buf2, sizeof(buf2));
|
||||
SG_VERIFY(n == 36 && string(buf2, n) == "klmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ");
|
||||
SG_CHECK_EQUAL(n, 36);
|
||||
SG_CHECK_EQUAL(string(buf2, 36), "klmnopqrstuvwxyz\nABCDEF\nGHIJK LMNOPQ");
|
||||
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_CHECK_EQUAL(ch, EOF);
|
||||
@@ -268,6 +293,60 @@ string compress(const string& dataToCompress,
|
||||
return compressedData_ss.str();
|
||||
}
|
||||
|
||||
// Test simgear::ZlibDecompressorIStreambuf::[x]sgetn(), asking the largest
|
||||
// possible amount of chars every time it is called (i.e., the largest value
|
||||
// that can be represented by std::streamsize).
|
||||
void test_ZlibDecompressorIStreambuf_readLargestPossibleAmount()
|
||||
{
|
||||
// Nothing special with these values
|
||||
constexpr std::size_t maxDataSize = 8192;
|
||||
std::istringstream input_ss(randomString(4096, maxDataSize));
|
||||
|
||||
simgear::ZlibCompressorIStream compIStream(
|
||||
input_ss, // input stream
|
||||
SGPath(), // this stream is not associated to a file
|
||||
9, // compression level
|
||||
simgear::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
nullptr, // dynamically allocate the input buffer
|
||||
230, // input buffer size
|
||||
nullptr, // dynamically allocate the output buffer
|
||||
120, // output buffer size
|
||||
1 // putback size
|
||||
);
|
||||
|
||||
// Decompressor stream buffer (std::streambuf subclass) that gets input data
|
||||
// from our compressor 'compIStream' (std::istream subclass)
|
||||
simgear::ZlibDecompressorIStreambuf decompSBuf(
|
||||
compIStream, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
|
||||
nullptr, 150, nullptr, 175, 2);
|
||||
|
||||
std::unique_ptr<char[]> buf(new char[maxDataSize]);
|
||||
std::ostringstream roundTripResult_ss;
|
||||
std::streamsize totalCharsToRead = input_ss.str().size();
|
||||
|
||||
while (totalCharsToRead > 0) {
|
||||
// Ask sgetn() the largest possible amount of chars. Of course, we know we
|
||||
// can't get more than maxDataSize, but this does exercise the code in
|
||||
// interesting ways due to the various types involved (zlib's uInt,
|
||||
// std::size_t and std::streamsize, which have various sizes depending on
|
||||
// the platform).
|
||||
std::streamsize nbCharsRead = decompSBuf.sgetn(
|
||||
&buf[0], std::numeric_limits<std::streamsize>::max());
|
||||
if (nbCharsRead == 0) {
|
||||
break; // no more data
|
||||
}
|
||||
|
||||
// The conversion to std::size_t is safe because decompSBuf.sgetn()
|
||||
// returned a non-negative value which, in this case, can't exceed
|
||||
// maxDataSize.
|
||||
roundTripResult_ss << string(&buf[0], streamsizeToSize_t((nbCharsRead)));
|
||||
}
|
||||
|
||||
SG_CHECK_EQUAL(decompSBuf.sgetc(), EOF);
|
||||
SG_CHECK_EQUAL(roundTripResult_ss.str(), input_ss.str());
|
||||
}
|
||||
|
||||
void test_formattedInputFromDecompressor()
|
||||
{
|
||||
cerr << "Testing ZlibDecompressorIStream >> std::string\n";
|
||||
@@ -275,12 +354,12 @@ void test_formattedInputFromDecompressor()
|
||||
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);
|
||||
lipsum, simgear::ZLibCompressionFormat::ZLIB, Z_BEST_COMPRESSION,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
|
||||
std::istringstream compressed_ss(compressed);
|
||||
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressed_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
compressed_ss, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
|
||||
inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), /* putback size */ 1);
|
||||
decompressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
@@ -319,15 +398,15 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
text_ss, SGPath(), Z_BEST_COMPRESSION,
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
simgear::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
|
||||
compInBuf, sizeof(compInBuf), compOutBuf, sizeof(compOutBuf),
|
||||
/* putback size */ 0);
|
||||
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
// Use the compressor (subclass of std::istream) as input to the decompressor
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressor, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
compressor, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
|
||||
decompInBuf, sizeof(decompInBuf), decompOutBuf, sizeof(decompOutBuf),
|
||||
/* putback size */ 3);
|
||||
decompressor.exceptions(std::ios_base::badbit);
|
||||
@@ -354,6 +433,14 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
decompressor.putback('Z');
|
||||
} catch (std::ios_base::failure) {
|
||||
gotException = true;
|
||||
} catch (const std::exception& e) {
|
||||
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11
|
||||
// ABI between headers and libraries. See bug#66145 for more details.
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145
|
||||
if (!strcmp(e.what(), "basic_ios::clear"))
|
||||
gotException = true;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
SG_VERIFY(gotException && decompressor.bad());
|
||||
}
|
||||
@@ -404,7 +491,10 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
string rest(buf2, nbCharsRead);
|
||||
do {
|
||||
decompressor.read(buf2, sizeof(buf2));
|
||||
rest += string(buf2, decompressor.gcount());
|
||||
// The conversion to std::size_t is safe because decompressor.read()
|
||||
// returns a non-negative value which, in this case, can't exceed
|
||||
// sizeof(buf2).
|
||||
rest += string(buf2, streamsizeToSize_t(decompressor.gcount()));
|
||||
} while (decompressor);
|
||||
|
||||
SG_CHECK_EQUAL(rest, " LMNOPQ");
|
||||
@@ -417,7 +507,6 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
// 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
|
||||
@@ -442,7 +531,7 @@ void roundTripWithIStreams(
|
||||
{
|
||||
const simgear::ZLibCompressionFormat decompFormat =
|
||||
(useAutoFormatForDecompression) ?
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_AUTODETECT : compressionFormat;
|
||||
simgear::ZLibCompressionFormat::AUTODETECT : compressionFormat;
|
||||
|
||||
std::istringstream lipsum_ss(lipsum);
|
||||
// This tests the optional dynamic buffer allocation in ZlibAbstractIStreambuf
|
||||
@@ -482,11 +571,12 @@ void test_RoundTripMultiWithIStreams()
|
||||
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 (auto format: {simgear::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibCompressionFormat::GZIP}) {
|
||||
for (int compressionLevel: {1, 4, 7, 9}) {
|
||||
for (auto memStrategy: {simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY}) {
|
||||
for (auto memStrategy: {
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY}) {
|
||||
for (std::size_t compInBufSize: {3, 4}) {
|
||||
for (std::size_t compOutBufSize: {3, 5}) {
|
||||
for (std::size_t decompInBufSize: {3, 4}) {
|
||||
@@ -505,9 +595,10 @@ void test_RoundTripMultiWithIStreams()
|
||||
}
|
||||
|
||||
{
|
||||
const auto format = simgear::ZLIB_COMPRESSION_FORMAT_ZLIB;
|
||||
const auto format = simgear::ZLibCompressionFormat::ZLIB;
|
||||
const int compressionLevel = Z_DEFAULT_COMPRESSION;
|
||||
const auto memStrategy = simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY;
|
||||
const auto memStrategy =
|
||||
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY;
|
||||
|
||||
for (std::size_t compInBufSize: {3, 4, 31, 256, 19475}) {
|
||||
for (std::size_t compOutBufSize: {3, 5, 9, 74, 4568}) {
|
||||
@@ -537,11 +628,12 @@ void test_RoundTripMultiWithIStreams()
|
||||
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;
|
||||
simgear::ZLibCompressionFormat::ZLIB :
|
||||
simgear::ZLibCompressionFormat::GZIP;
|
||||
|
||||
roundTripWithIStreams(
|
||||
compFormat, Z_BEST_COMPRESSION, simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
compFormat, Z_BEST_COMPRESSION,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
|
||||
compInBufSize, compOutBufSize, decompInBufSize, decompOutBufSize,
|
||||
compPutbackSize, decompPutbackSize,
|
||||
/* automatic format detection for decompression */ true);
|
||||
@@ -550,13 +642,91 @@ void test_RoundTripMultiWithIStreams()
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function showing how to return a (unique_ptr to a)
|
||||
// ZlibCompressorIStream instance, that keeps a reference to its data source
|
||||
// as long as the ZlibCompressorIStream instance is alive. Thus, calling code
|
||||
// doesn't have to worry about the lifetime of said data source (here, an
|
||||
// std::istringstream instance).
|
||||
std::unique_ptr<simgear::ZlibCompressorIStream>
|
||||
IStreamConstructorWithSinkSemantics_compressorFactory(const string& str)
|
||||
{
|
||||
std::unique_ptr<std::istringstream> iss(new std::istringstream(str));
|
||||
|
||||
// The returned compressor object retains a “reference” (of unique_ptr type)
|
||||
// to the std::istringstream object pointed to by 'iss' as long as it is
|
||||
// alive. When the returned compressor object (wrapped in a unique_ptr) is
|
||||
// destroyed, this std::istringstream object will be automatically
|
||||
// destroyed too.
|
||||
//
|
||||
// Note: it's an implementation detail, but this test also indirectly
|
||||
// exercises the ZlibCompressorIStreambuf constructor taking an
|
||||
// argument of type std::unique_ptr<std::istream>.
|
||||
return std::unique_ptr<simgear::ZlibCompressorIStream>(
|
||||
new simgear::ZlibCompressorIStream(std::move(iss)));
|
||||
}
|
||||
|
||||
void test_IStreamConstructorWithSinkSemantics()
|
||||
{
|
||||
cerr << "Testing the unique_ptr-based ZlibCompressorIStream constructor\n";
|
||||
|
||||
string someString = randomString(4096, 8192); // arbitrary values
|
||||
// This shows how to get a new compressor or decompressor object from a
|
||||
// factory function. Of course, we could create the object directly on the
|
||||
// stack without using a separate function!
|
||||
std::unique_ptr<simgear::ZlibCompressorIStream> compressor =
|
||||
IStreamConstructorWithSinkSemantics_compressorFactory(someString);
|
||||
compressor->exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
// Use the compressor as input to the decompressor (pipeline). The
|
||||
// decompressor uses read() with chunks that are as large as possible given
|
||||
// the available space in its input buffer. These read() calls are served by
|
||||
// ZlibCompressorIStreambuf::xsgetn(), which is efficient. We won't need the
|
||||
// compressor afterwards, so let's just std::move() its unique_ptr.
|
||||
simgear::ZlibDecompressorIStream decompressor(std::move(compressor));
|
||||
decompressor.exceptions(std::ios_base::badbit);
|
||||
|
||||
std::ostringstream roundTripResult;
|
||||
// Of course, you may want to adjust bufSize depending on the application.
|
||||
static constexpr std::size_t bufSize = 1024;
|
||||
std::unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
// Relatively efficient way of reading from the decompressor (modulo
|
||||
// possible adjustments to 'bufSize', of course). The decompressed data is
|
||||
// first written to 'buf', then copied to 'roundTripResult'. There is no
|
||||
// other useless copy via, for instance, an intermediate std::string object,
|
||||
// as would be the case if we used std::string(buf.get(), bufSize).
|
||||
//
|
||||
// Of course, ideally 'roundTripResult' would directly pull from
|
||||
// 'decompressor' without going through 'buf', but I don't think this is
|
||||
// possible with std::stringstream and friends. Such an optimized data flow
|
||||
// is however straightforward to implement if you replace 'roundTripResult'
|
||||
// with a custom data sink that calls decompressor.read().
|
||||
do {
|
||||
decompressor.read(buf.get(), bufSize);
|
||||
if (decompressor.gcount() > 0) { // at least one char could be read
|
||||
roundTripResult.write(buf.get(), decompressor.gcount());
|
||||
}
|
||||
} while (decompressor && roundTripResult);
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(decompressor.eof() && !decompressor.bad());
|
||||
// Because of std::ostringstream::write(), 'roundTripResult' might have its
|
||||
// failbit or badbit set, either of which would indicate a real problem.
|
||||
SG_VERIFY(roundTripResult);
|
||||
SG_CHECK_EQUAL(roundTripResult.str(), someString);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
test_pipeCompOrDecompIStreambufIntoOStream();
|
||||
test_StreambufBasicOperations();
|
||||
test_ZlibDecompressorIStreambuf_readLargestPossibleAmount();
|
||||
test_RoundTripMultiWithIStreams();
|
||||
test_formattedInputFromDecompressor();
|
||||
test_ZlibDecompressorIStream_readPutbackEtc();
|
||||
test_IStreamConstructorWithSinkSemantics();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
$Id: netBuffer.cxx 1568 2002-09-02 06:05:49Z sjbaker $
|
||||
*/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "sg_netBuffer.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
// to write or something...]
|
||||
// Maybe assert valid handle, too?
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "sg_netChannel.hxx"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
@@ -78,6 +78,9 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
|
||||
const char * EXISTING_RECORD = argc > 1 ? argv[1] : "terrasync.flightgear.org";
|
||||
const char * QSERVICE = argc > 2 ? argv[2] : "https+ws20";
|
||||
|
||||
Watchdog watchdog;
|
||||
watchdog.start(100);
|
||||
|
||||
@@ -98,8 +101,7 @@ int main(int argc, char* argv[])
|
||||
cout << "done" << endl;
|
||||
}
|
||||
|
||||
#define EXISTING_RECORD "terrasync.flightgear.org"
|
||||
cout << "test existing NAPTR: " EXISTING_RECORD << endl;
|
||||
cout << "test existing NAPTR: " << EXISTING_RECORD << endl;
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
@@ -110,23 +112,24 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if( r->isTimeout() ) {
|
||||
cerr << "timeout testing existing record " EXISTING_RECORD << endl;
|
||||
cerr << "timeout testing existing record " << EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(naptrRequest->entries.empty()) {
|
||||
cerr << "no results for " EXISTING_RECORD << endl;
|
||||
cerr << "no results for " << EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cout << "test for ascending preference/order" << endl;
|
||||
int order = -1, preference = -1;
|
||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); it != naptrRequest->entries.end(); ++it ) {
|
||||
cout << "NAPTR " << (*it)->order << " " << (*it)->preference << " '" << (*it)->service << "' '" << (*it)->regexp << "' '" << (*it)->replacement << "'" << endl;
|
||||
// currently only support "U" which implies empty replacement
|
||||
SG_CHECK_EQUAL((*it)->flags, "U" );
|
||||
SG_CHECK_EQUAL(naptrRequest->entries[0]->replacement, "" );
|
||||
|
||||
// currently only support ws20
|
||||
SG_CHECK_EQUAL((*it)->service, "ws20" );
|
||||
// currently only support ws20, disable temporarily
|
||||
//SG_CHECK_EQUAL((*it)->service, "ws20" );
|
||||
|
||||
if( (*it)->order < order ) {
|
||||
cerr << "NAPTR entries not ascending for field 'order'" << endl;
|
||||
@@ -154,6 +157,29 @@ int main(int argc, char* argv[])
|
||||
|
||||
}
|
||||
}
|
||||
cout << "test existing NAPTR with explicit qservice: " << QSERVICE << endl;
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
|
||||
naptrRequest->qservice = QSERVICE;
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
cl.makeRequest(r);
|
||||
while( !r->isComplete() && !r->isTimeout()) {
|
||||
SGTimeStamp::sleepForMSec(200);
|
||||
cl.update(0);
|
||||
}
|
||||
|
||||
if( r->isTimeout() ) {
|
||||
cerr << "timeout testing existing record " << EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(naptrRequest->entries.empty()) {
|
||||
cerr << "no results for " << EXISTING_RECORD << endl;
|
||||
//return EXIT_FAILURE; // not yet a failure - probably add this for 2017.4 and create DNS entries
|
||||
}
|
||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); it != naptrRequest->entries.end(); ++it ) {
|
||||
cout << "NAPTR " << (*it)->order << " " << (*it)->preference << " '" << (*it)->service << "' '" << (*it)->regexp << "' '" << (*it)->replacement << "'" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "test non-existing NAPTR" << endl;
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Test harness.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "untar.hxx"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -341,18 +341,16 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) : simd4(_mm_setzero_ps()) {}
|
||||
simd4_t(float f) {}
|
||||
simd4_t(void) { simd4 = _mm_setzero_ps(); }
|
||||
simd4_t(float f) { simd4 = _mm_set1_ps(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) {}
|
||||
simd4_t(const __vec4f_t v) {}
|
||||
simd4_t(const simd4_t<float,4>& v) {}
|
||||
simd4_t(const simd4_t<float,3>& v) {}
|
||||
simd4_t(const simd4_t<float,2>& v) {}
|
||||
simd4_t(const __m128& v) {
|
||||
simd4 = v;
|
||||
}
|
||||
simd4_t(float x, float y, float z, float w) { simd4 = _mm_set_ps(w,z,y,x); }
|
||||
simd4_t(const __vec4f_t v) { simd4 = _mm_loadu_ps(v); }
|
||||
simd4_t(const simd4_t<float,4>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<float,3>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<float,2>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const __m128& v) { simd4 = v; }
|
||||
|
||||
inline const __m128 (&v4(void) const) {
|
||||
return simd4;
|
||||
@@ -583,18 +581,18 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) : simd4(_mm256_setzero_pd()) {}
|
||||
simd4_t(double d) {}
|
||||
simd4_t(void) { simd4 = _mm256_setzero_pd(); }
|
||||
simd4_t(double d) { simd4 = _mm256_set1_pd(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_t(const __vec4d_t v) {}
|
||||
simd4_t(const simd4_t<double,4>& v) {}
|
||||
simd4_t(const simd4_t<double,3>& v) {}
|
||||
simd4_t(const simd4_t<double,2>& v) {}
|
||||
simd4_t(const __m256d& v) {
|
||||
simd4 = v;
|
||||
simd4_t(double x, double y, double z, double w) {
|
||||
simd4 = _mm256_set_pd(w,z,y,x);
|
||||
}
|
||||
simd4_t(const __vec4d_t v) { simd4 = _mm256_loadu_pd(v); }
|
||||
simd4_t(const simd4_t<double,4>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<double,3>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<double,2>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const __m256d& v) { simd4 = v; }
|
||||
|
||||
inline const __m256d (&v4(void) const) {
|
||||
return simd4;
|
||||
@@ -819,14 +817,24 @@ private:
|
||||
|
||||
public:
|
||||
simd4_t(void) { simd4[0] = simd4[1] = _mm_setzero_pd(); }
|
||||
simd4_t(double d) {}
|
||||
simd4_t(double d) { simd4[0] = simd4[1] = _mm_set1_pd(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_t(const __vec4d_t v) {}
|
||||
simd4_t(const simd4_t<double,4>& v) {}
|
||||
simd4_t(const simd4_t<double,3>& v) {}
|
||||
simd4_t(const simd4_t<double,2>& v) {}
|
||||
simd4_t(double x, double y, double z, double w) {
|
||||
simd4[0] = _mm_set_pd(y,x); simd4[1] = _mm_set_pd(w,z);
|
||||
}
|
||||
simd4_t(const __vec4d_t v) {
|
||||
simd4[0] = _mm_loadu_pd(v); simd4[1] = _mm_loadu_pd(v+2);
|
||||
}
|
||||
simd4_t(const simd4_t<double,4>& v) {
|
||||
simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1];
|
||||
}
|
||||
simd4_t(const simd4_t<double,3>& v) {
|
||||
simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1];
|
||||
}
|
||||
simd4_t(const simd4_t<double,2>& v) {
|
||||
simd4[0] = v.v4()[0]; simd4[1] = _mm_setzero_pd();
|
||||
}
|
||||
simd4_t(const __m128d v[2]) {
|
||||
simd4[0] = v[0];
|
||||
simd4[1] = v[1];
|
||||
@@ -1101,18 +1109,16 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) : simd4(_mm_setzero_si128()) {}
|
||||
simd4_t(int i) {}
|
||||
simd4_t(void) { simd4 = _mm_setzero_si128(); }
|
||||
simd4_t(int i) { simd4 = _mm_set1_epi32(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) {}
|
||||
simd4_t(const __vec4i_t v) {}
|
||||
simd4_t(const simd4_t<int,4>& v) {}
|
||||
simd4_t(const simd4_t<int,3>& v) {}
|
||||
simd4_t(const simd4_t<int,2>& v) {}
|
||||
simd4_t(const __m128i& v) {
|
||||
simd4 = v;
|
||||
}
|
||||
simd4_t(int x, int y, int z, int w) { simd4 = _mm_set_epi32(w,z,y,x); }
|
||||
simd4_t(const __vec4i_t v) { simd4 = _mm_loadu_si128((const __m128i*)v); }
|
||||
simd4_t(const simd4_t<int,4>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<int,3>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const simd4_t<int,2>& v) { simd4 = v.v4(); }
|
||||
simd4_t(const __m128i& v) { simd4 = v; }
|
||||
|
||||
inline __m128i (&v4(void)) {
|
||||
return simd4;
|
||||
|
||||
@@ -7,6 +7,7 @@ set(HEADERS
|
||||
ResourceManager.hxx
|
||||
SimpleMarkdown.hxx
|
||||
SVGpreserveAspectRatio.hxx
|
||||
argparse.hxx
|
||||
interpolator.hxx
|
||||
make_new.hxx
|
||||
sg_dir.hxx
|
||||
@@ -17,6 +18,7 @@ set(HEADERS
|
||||
strutils.hxx
|
||||
tabbed_values.hxx
|
||||
texcoord.hxx
|
||||
test_macros.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -24,6 +26,7 @@ set(SOURCES
|
||||
ResourceManager.cxx
|
||||
SimpleMarkdown.cxx
|
||||
SVGpreserveAspectRatio.cxx
|
||||
argparse.cxx
|
||||
interpolator.cxx
|
||||
sg_dir.cxx
|
||||
sg_path.cxx
|
||||
@@ -45,6 +48,10 @@ simgear_component(misc misc "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(test_argparse argparse_test.cxx)
|
||||
target_link_libraries(test_argparse ${TEST_LIBS})
|
||||
add_test(argparse ${EXECUTABLE_OUTPUT_PATH}/test_argparse)
|
||||
|
||||
add_executable(test_CSSBorder CSSBorder_test.cxx)
|
||||
add_test(CSSBorder ${EXECUTABLE_OUTPUT_PATH}/test_CSSBorder)
|
||||
target_link_libraries(test_CSSBorder ${TEST_LIBS})
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "SVGpreserveAspectRatio.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
391
simgear/misc/argparse.cxx
Normal file
391
simgear/misc/argparse.cxx
Normal file
@@ -0,0 +1,391 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// argparse.cxx --- Simple, generic parser for command-line arguments
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility> // std::pair, std::move()
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstring> // std::strcmp()
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "argparse.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
namespace argparse
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * Base class for custom exceptions *
|
||||
// ***************************************************************************
|
||||
Error::Error(const string& message, const std::string& origin)
|
||||
: sg_exception("Argument parser error: " + message, origin)
|
||||
{ }
|
||||
|
||||
Error::Error(const char* message, const char* origin)
|
||||
: Error(string(message), string(origin))
|
||||
{ }
|
||||
|
||||
// ***************************************************************************
|
||||
// * OptionDesc class *
|
||||
// ***************************************************************************
|
||||
OptionDesc::OptionDesc(
|
||||
const string& optionId, std::vector<char> shortAliases,
|
||||
std::vector<string> longAliases, OptionArgType argumentType)
|
||||
: _id(optionId),
|
||||
_shortAliases(shortAliases),
|
||||
_longAliases(longAliases),
|
||||
_argumentType(argumentType)
|
||||
{ }
|
||||
|
||||
const std::string& OptionDesc::id() const
|
||||
{ return _id; }
|
||||
|
||||
const std::vector<char>& OptionDesc::shortAliases() const
|
||||
{ return _shortAliases; }
|
||||
|
||||
const std::vector<std::string>& OptionDesc::longAliases() const
|
||||
{ return _longAliases; }
|
||||
|
||||
OptionArgType OptionDesc::argumentType() const
|
||||
{ return _argumentType; }
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// * OptionValue class *
|
||||
// ***************************************************************************
|
||||
OptionValue::OptionValue(shared_ptr<const OptionDesc> optionDesc,
|
||||
const string& passedAs, const string& value,
|
||||
bool hasValue)
|
||||
: _optionDesc(std::move(optionDesc)),
|
||||
_passedAs(passedAs),
|
||||
_value(value),
|
||||
_hasValue(hasValue)
|
||||
{ }
|
||||
|
||||
shared_ptr<const OptionDesc> OptionValue::optionDesc() const
|
||||
{ return _optionDesc; } // return a copy of the shared_ptr
|
||||
|
||||
void OptionValue::setOptionDesc(shared_ptr<const OptionDesc> descPtr)
|
||||
{ _optionDesc = std::move(descPtr); }
|
||||
|
||||
string OptionValue::passedAs() const
|
||||
{ return _passedAs; }
|
||||
|
||||
void OptionValue::setPassedAs(const string& passedAs)
|
||||
{ _passedAs = passedAs; }
|
||||
|
||||
string OptionValue::value() const
|
||||
{ return _value; }
|
||||
|
||||
void OptionValue::setValue(const string& value)
|
||||
{ _value = value; }
|
||||
|
||||
bool OptionValue::hasValue() const
|
||||
{ return _hasValue; }
|
||||
|
||||
void OptionValue::setHasValue(bool hasValue)
|
||||
{ _hasValue = hasValue; }
|
||||
|
||||
const string OptionValue::id() const
|
||||
{
|
||||
const auto desc = optionDesc();
|
||||
return (desc) ? desc->id() : string();
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// * ArgumentParser class *
|
||||
// ***************************************************************************
|
||||
|
||||
// Static utility method.
|
||||
std::vector<char>
|
||||
ArgumentParser::removeHyphens(const std::vector<string>& shortAliases,
|
||||
std::vector<string>& longAliases)
|
||||
{
|
||||
std::vector<char> shortAliasesCharVec;
|
||||
shortAliasesCharVec.reserve(shortAliases.size());
|
||||
|
||||
for (const string& opt: shortAliases) {
|
||||
if (opt.size() != 2 || opt[0] != '-' || opt[1] == '-' || opt[1] > 127) {
|
||||
throw Error("unexpected form for a short option: '" + opt + "' (expecting "
|
||||
"a string of size 2 whose first character is a hyphen and "
|
||||
"second character an ASCII char that is not a hyphen)");
|
||||
}
|
||||
|
||||
shortAliasesCharVec.emplace_back(opt[1]); // emplace the char after hyphen
|
||||
}
|
||||
|
||||
for (string& longOpt: longAliases) {
|
||||
if (longOpt.size() < 3 ||
|
||||
!simgear::strutils::starts_with(longOpt, string("--"))) {
|
||||
throw Error("unexpected form for a long option: '" + longOpt + "' "
|
||||
"(expecting a string of size 3 or more that starts with "
|
||||
"two hyphens)");
|
||||
}
|
||||
|
||||
longOpt.erase(0, 2); // remove the two leading hyphens
|
||||
}
|
||||
|
||||
return shortAliasesCharVec;
|
||||
}
|
||||
|
||||
void
|
||||
ArgumentParser::addOption(const string& optionId,
|
||||
OptionArgType argType,
|
||||
std::vector<string> shortAliases,
|
||||
std::vector<string> longAliases)
|
||||
{
|
||||
// Remove the leading dashes and do a sanity check for these arguments
|
||||
std::vector<char> shortAliasesCharVec = removeHyphens(shortAliases,
|
||||
longAliases);
|
||||
|
||||
const auto desc_p = std::make_shared<const OptionDesc>(
|
||||
optionId, std::move(shortAliasesCharVec), std::move(longAliases), argType);
|
||||
|
||||
for (const char c: desc_p->shortAliases()) {
|
||||
if (!_shortOptionMap.emplace(c, desc_p).second) {
|
||||
throw Error(
|
||||
"trying to add option '-" + string(1, c) + "', however it is already "
|
||||
"in the short option map");
|
||||
}
|
||||
}
|
||||
|
||||
for (const string& longOpt: desc_p->longAliases()) {
|
||||
if (!_longOptionMap.emplace(longOpt, desc_p).second) {
|
||||
throw Error(
|
||||
"trying to add option '--" + longOpt + "', however it is already in "
|
||||
"the long option map");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ArgumentParser::addOption(const string& optionId, OptionArgType argumentType,
|
||||
string shortOpt, string longOpt)
|
||||
{
|
||||
std::vector<string> shortOptList;
|
||||
std::vector<string> longOptList;
|
||||
|
||||
if (!shortOpt.empty()) {
|
||||
shortOptList.push_back(std::move(shortOpt));
|
||||
}
|
||||
|
||||
if (!longOpt.empty()) {
|
||||
longOptList.push_back(std::move(longOpt));
|
||||
}
|
||||
|
||||
addOption(optionId, argumentType, std::move(shortOptList),
|
||||
std::move(longOptList));
|
||||
}
|
||||
|
||||
std::pair< std::vector<OptionValue>, std::vector<string> >
|
||||
ArgumentParser::parseArgs(int argc, const char *const *argv) const
|
||||
{
|
||||
std::pair< std::vector<OptionValue>, std::vector<string> > res;
|
||||
std::vector<OptionValue>& optsWithValues = res.first;
|
||||
std::vector<string>& nonOptionArgs = res.second;
|
||||
bool inOptions = true;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
// Decode from command line encoding
|
||||
const string currentArg = cmdEncToUtf8(argv[i]);
|
||||
|
||||
if ((inOptions) && (currentArg == "--")) {
|
||||
// We found the end-of-options delimiter
|
||||
inOptions = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inOptions) {
|
||||
if (currentArg.size() >= 2 && currentArg[0] == '-') {
|
||||
if (currentArg[1] == '-') {
|
||||
i += readLongOption(argc, argv, currentArg, i+1, optsWithValues);
|
||||
} else {
|
||||
i += readShortOptions(argc, argv, currentArg, i+1, optsWithValues);
|
||||
}
|
||||
} else {
|
||||
// The argument is neither an option, nor a cluster of short options.
|
||||
inOptions = false;
|
||||
nonOptionArgs.push_back(currentArg);
|
||||
}
|
||||
} else {
|
||||
nonOptionArgs.push_back(currentArg);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Static method
|
||||
string ArgumentParser::cmdEncToUtf8(const string& s)
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
// Untested code path. Comments and/or testing by Windows people welcome.
|
||||
return simgear::strutils::convertWindowsLocal8BitToUtf8(s);
|
||||
#else
|
||||
// XXX This assumes UTF-8 encoding for command line arguments on non-Windows
|
||||
// platforms. Unfortunately, the current (April 2017) standard C++ API for
|
||||
// encoding conversions has big problems (cf.
|
||||
// <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html>).
|
||||
// Should be fixed when we have a good way to do such conversions.
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the number of arguments used by the option value, if any (i.e., how
|
||||
// much the caller should shift to resume arguments processing).
|
||||
int ArgumentParser::readLongOption(int argc, const char *const *argv,
|
||||
const string& currentArg, int nextArgIdx,
|
||||
std::vector<OptionValue>& optsWithValues)
|
||||
const
|
||||
{
|
||||
const string s = currentArg.substr(2); // skip the two initial dashes
|
||||
|
||||
// UTF-8 guarantees that ASCII bytes (here, '=') cannot be part of the
|
||||
// encoding of a non-ASCII character.
|
||||
std::size_t optEnd = s.find('=');
|
||||
string opt = s.substr(0, optEnd);
|
||||
|
||||
const auto mapElt = _longOptionMap.find(opt);
|
||||
if (mapElt != _longOptionMap.end()) {
|
||||
const shared_ptr<const OptionDesc>& optDesc = mapElt->second;
|
||||
OptionValue optVal(optDesc, string("--") + opt);
|
||||
|
||||
switch (optDesc->argumentType()) {
|
||||
case OptionArgType::NO_ARGUMENT:
|
||||
optVal.setHasValue(false);
|
||||
optsWithValues.push_back(std::move(optVal));
|
||||
return 0;
|
||||
case OptionArgType::OPTIONAL_ARGUMENT: // pass through
|
||||
case OptionArgType::MANDATORY_ARGUMENT:
|
||||
if (optEnd != string::npos) {
|
||||
// The optional value is present in the same command line
|
||||
// argument as the option name (syntax '--option=value').
|
||||
optVal.setHasValue(true);
|
||||
optVal.setValue(s.substr(optEnd + 1));
|
||||
optsWithValues.push_back(std::move(optVal));
|
||||
return 0;
|
||||
} else if (nextArgIdx < argc &&
|
||||
(argv[nextArgIdx][0] != '-' ||
|
||||
!std::strcmp(argv[nextArgIdx], "-"))) {
|
||||
// The optional value is present as a separate command line argument
|
||||
// (syntax '--option value').
|
||||
optVal.setHasValue(true);
|
||||
optVal.setValue(cmdEncToUtf8(argv[nextArgIdx]));
|
||||
optsWithValues.push_back(std::move(optVal));
|
||||
return 1;
|
||||
} else if (optDesc->argumentType() ==
|
||||
OptionArgType::OPTIONAL_ARGUMENT) {
|
||||
// No argument (value) can be found for the option
|
||||
optVal.setHasValue(false);
|
||||
optsWithValues.push_back(std::move(optVal));
|
||||
return 0;
|
||||
} else {
|
||||
assert(optDesc->argumentType() == OptionArgType::MANDATORY_ARGUMENT);
|
||||
throw InvalidUserInput("option '" + optVal.passedAs() + "' requires an "
|
||||
"argument, but none was provided");
|
||||
}
|
||||
default:
|
||||
throw sg_error("This piece of code should be unreachable.");
|
||||
}
|
||||
} else {
|
||||
throw InvalidUserInput("invalid option: '--" + opt + "'");
|
||||
}
|
||||
}
|
||||
|
||||
int ArgumentParser::readShortOptions(int argc, const char *const *argv,
|
||||
const string& currentArg, int nextArgIdx,
|
||||
std::vector<OptionValue>& optsWithValues)
|
||||
const
|
||||
{
|
||||
shared_ptr<const OptionDesc> optDesc;
|
||||
const string s = currentArg.substr(1); // skip the initial dash
|
||||
std::size_t i = 0; // index inside s
|
||||
|
||||
// Read all options taking no argument in 'currentArg'; stop at the first
|
||||
// taking an optional or mandatory argument.
|
||||
for (/* empty */; i < s.size(); i++) {
|
||||
const auto mapElt = _shortOptionMap.find(s[i]);
|
||||
if (mapElt != _shortOptionMap.end()) {
|
||||
optDesc = mapElt->second;
|
||||
|
||||
if (optDesc->argumentType() == OptionArgType::NO_ARGUMENT) {
|
||||
optsWithValues.emplace_back(optDesc, string("-") + s[i], string(),
|
||||
false /* no value */);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw InvalidUserInput(string("invalid option: '-") + s[i] + "'");
|
||||
}
|
||||
}
|
||||
|
||||
if (i == s.size()) {
|
||||
// The command line argument in 'currentArg' was fully read and only
|
||||
// contains options that take no argument.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We've already “eaten” all options taking no argument in 'currentArg'.
|
||||
assert(optDesc->argumentType() == OptionArgType::OPTIONAL_ARGUMENT ||
|
||||
optDesc->argumentType() == OptionArgType::MANDATORY_ARGUMENT);
|
||||
|
||||
if (i + 1 < s.size()) {
|
||||
// The option has a value at the end of 'currentArg': s.substr(i+1)
|
||||
optsWithValues.emplace_back(optDesc, string("-") + s[i], s.substr(i+1),
|
||||
true /* hasValue */);
|
||||
return 0;
|
||||
} else if (nextArgIdx < argc &&
|
||||
(argv[nextArgIdx][0] != '-' ||
|
||||
!std::strcmp(argv[nextArgIdx], "-"))) {
|
||||
assert(i + 1 == s.size());
|
||||
// The option is at the end of 'currentArg' and has a value:
|
||||
// argv[nextArgIdx].
|
||||
optsWithValues.emplace_back(optDesc, string("-") + s[i],
|
||||
cmdEncToUtf8(argv[nextArgIdx]),
|
||||
true /* hasValue */);
|
||||
return 1;
|
||||
} else if (optDesc->argumentType() ==
|
||||
OptionArgType::OPTIONAL_ARGUMENT) {
|
||||
// No argument (value) can be found for the option
|
||||
optsWithValues.emplace_back(optDesc, string("-") + s[i], string(),
|
||||
false /* no value */);
|
||||
return 0;
|
||||
} else {
|
||||
assert(optDesc->argumentType() == OptionArgType::MANDATORY_ARGUMENT);
|
||||
throw InvalidUserInput(string("option '-") + s[i] + "' requires an "
|
||||
"argument, but none was provided");
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace argparse
|
||||
|
||||
} // of namespace simgear
|
||||
281
simgear/misc/argparse.hxx
Normal file
281
simgear/misc/argparse.hxx
Normal file
@@ -0,0 +1,281 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// argparse.hxx --- Simple, generic parser for command-line arguments
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef _SIMGEAR_ARGPARSE_HXX_
|
||||
#define _SIMGEAR_ARGPARSE_HXX_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <utility> // std::pair
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
// Usage example:
|
||||
//
|
||||
// using simgear::argparse::OptionArgType;
|
||||
//
|
||||
// simgear::argparse::ArgumentParser parser;
|
||||
// parser.addOption("root option", OptionArgType::MANDATORY_ARGUMENT,
|
||||
// "", "--root");
|
||||
// parser.addOption("test option", OptionArgType::NO_ARGUMENT, "-t", "--test");
|
||||
//
|
||||
// const auto res = parser.parseArgs(argc, argv);
|
||||
//
|
||||
// for (const auto& opt: res.first) {
|
||||
// std::cerr << "Got option '" << opt.id() << "' as '" << opt.passedAs() <<
|
||||
// "'" << ((opt.hasValue()) ? " with value '" + opt.value() + "'" : "") <<
|
||||
// "\n";
|
||||
// }
|
||||
//
|
||||
// for (const auto& arg: res.second) {
|
||||
// std::cerr << "Got non-option argument '" << arg << "'\n";
|
||||
// }
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
namespace argparse
|
||||
{
|
||||
|
||||
// Custom exception classes
|
||||
class Error : public sg_exception
|
||||
{
|
||||
public:
|
||||
explicit Error(const std::string& message,
|
||||
const std::string& origin = std::string());
|
||||
explicit Error(const char* message, const char* origin = nullptr);
|
||||
};
|
||||
|
||||
class InvalidUserInput : public Error
|
||||
{
|
||||
using Error::Error; // inherit all constructors
|
||||
};
|
||||
|
||||
|
||||
enum class OptionArgType {
|
||||
NO_ARGUMENT = 0,
|
||||
OPTIONAL_ARGUMENT,
|
||||
MANDATORY_ARGUMENT
|
||||
};
|
||||
|
||||
// All strings inside this class are encoded in UTF-8.
|
||||
class OptionDesc
|
||||
{
|
||||
public:
|
||||
explicit OptionDesc(const std::string& optionId,
|
||||
std::vector<char> shortAliases,
|
||||
std::vector<std::string> longAliases,
|
||||
OptionArgType argumentType);
|
||||
|
||||
// Simple getters for the private members
|
||||
const std::string& id() const;
|
||||
const std::vector<char>& shortAliases() const;
|
||||
const std::vector<std::string>& longAliases() const;
|
||||
OptionArgType argumentType() const;
|
||||
|
||||
private:
|
||||
// Option identifier, invisible to the end user. Used to easily refer to the
|
||||
// option despite the various forms it may take (short and/or long aliases).
|
||||
std::string _id;
|
||||
// Each element of _shortAliases must be an ASCII character. For instance,
|
||||
// 'o' for an option called '-o'.
|
||||
std::vector<char> _shortAliases;
|
||||
// Each element of _longAliases should be the name of a long option, with
|
||||
// the two leading dashes removed. For instance, 'generate-foobar' for an
|
||||
// option named '--generate-foobar'.
|
||||
std::vector<std::string> _longAliases;
|
||||
OptionArgType _argumentType;
|
||||
};
|
||||
|
||||
// All strings inside this class are encoded in UTF-8.
|
||||
class OptionValue
|
||||
{
|
||||
public:
|
||||
explicit OptionValue(std::shared_ptr<const OptionDesc> optionDesc,
|
||||
const std::string& passedAs,
|
||||
const std::string& value = std::string(),
|
||||
bool hasValue = false);
|
||||
|
||||
// Simple getters/accessors for the private members
|
||||
std::shared_ptr<const OptionDesc> optionDesc() const;
|
||||
std::string passedAs() const;
|
||||
std::string value() const;
|
||||
bool hasValue() const;
|
||||
|
||||
// The corresponding setters
|
||||
void setOptionDesc(std::shared_ptr<const OptionDesc>);
|
||||
void setPassedAs(const std::string&);
|
||||
void setValue(const std::string&);
|
||||
void setHasValue(bool);
|
||||
|
||||
// For convenience: get the option ID from the result of optionDesc()
|
||||
const std::string id() const;
|
||||
|
||||
private:
|
||||
// Pointer to the option descriptor.
|
||||
std::shared_ptr<const OptionDesc> _optionDesc;
|
||||
// Exact option passed (e.g., -f or --foobar).
|
||||
std::string _passedAs;
|
||||
// Value given for the option, if any (otherwise, the empty string).
|
||||
std::string _value;
|
||||
// Tells whether the option has been given a value. This is of course mainly
|
||||
// useful for options taking an *optional* argument. The value in question
|
||||
// can be the empty string, if given on a separate command line argument
|
||||
// from the option.
|
||||
bool _hasValue;
|
||||
};
|
||||
|
||||
|
||||
// Main class for command line processing. Every string coming out of it is
|
||||
// encoded in UTF-8.
|
||||
class ArgumentParser
|
||||
{
|
||||
public:
|
||||
// Register an option, with zero or more short aliases (e.g., -a, -u, - F)
|
||||
// and zero or more long aliases (e.g., --foobar, --barnum, --bleh). The
|
||||
// option may take no argument, or one optional argument, or one mandatory
|
||||
// argument. The 'optionId' is used to refer to the option in a clear and
|
||||
// simple way, even in the presence of several short or long aliases. It is
|
||||
// thus visible to the programmer using this API, but not to users of the
|
||||
// command line interface being implemented.
|
||||
//
|
||||
// Note: this method and all its overloads take options in the form "-o" or
|
||||
// "--foobar" (as std::string instances). While it would be possible
|
||||
// to only require a char for each short option and to take long
|
||||
// option declarations without the two leading dashes, the API chosen
|
||||
// here should lead to more readable and searchable user code.
|
||||
//
|
||||
// shortAliases: each element should consist of two characters: an ASCII
|
||||
// hyphen (-) followed by an ASCII character.
|
||||
// longAliases: each element should be a string in UTF-8 encoding, starting
|
||||
// with two ASCII/UTF-8 hyphens (U+002D).
|
||||
//
|
||||
// This API could be extended to automatically generate --help output from
|
||||
// strings passed to addOption().
|
||||
void addOption(const std::string& optionId,
|
||||
OptionArgType argumentType,
|
||||
std::vector<std::string> shortAliases,
|
||||
std::vector<std::string> longAliases);
|
||||
// Convenience overload that should be enough for most cases. To register
|
||||
// only a short option or only a long option, simply pass the empty string
|
||||
// for the corresponding parameter.
|
||||
void addOption(const std::string& optionId,
|
||||
OptionArgType argumentType,
|
||||
std::string shortOpt = std::string(),
|
||||
std::string longOpt = std::string());
|
||||
|
||||
// Parse arguments from an argc/argv pair of variables. 'argc' should be the
|
||||
// number of elements in 'argv', the first of which is ignored for the sake
|
||||
// of options and arguments extraction (since it normally holds the program
|
||||
// name).
|
||||
//
|
||||
// Note: this “number of elements” doesn't count the usual---and completely
|
||||
// unneeded here---final null pointer.
|
||||
//
|
||||
// Short options may be grouped in the usual way. For instance, if '-x',
|
||||
// '-z' and '-f' are three short options, the first two taking no argument
|
||||
// and '-f' taking one mandatory argument, then both '-xzf bar' and
|
||||
// '-xzfbar' are equivalent to '-x -z -f bar' as well as to '-x -z -fbar'
|
||||
// ('bar' being the value taken by option '-f').
|
||||
//
|
||||
// Long options are handled in the usual way too:
|
||||
//
|
||||
// '--foobar' for an option taking no argument
|
||||
//
|
||||
// '--foobar=value' for an option taking an optional or mandatory
|
||||
// or '--foobar value' argument (two separate command line arguments in the
|
||||
// second case)
|
||||
//
|
||||
// Long option names may contain spaces, though this is extremely uncommon
|
||||
// and inconvenient for users. Any option argument (be it for a long or a
|
||||
// short option) may contain spaces, as expected.
|
||||
//
|
||||
// As usual too, the special '--' argument consisting of two ASCII/UTF-8
|
||||
// hyphens, can be used to cause all subsequent arguments to be treated as
|
||||
// non-option arguments, regardless of whether they start with a hyphen or
|
||||
// not. In the absence of this special argument, the first argument that is
|
||||
// not the value of an option and is either a single hyphen, or doesn't
|
||||
// start with a hyphen, marks the end of options. This and all subsequent
|
||||
// arguments are read as non-option arguments.
|
||||
//
|
||||
// Return a pair containing:
|
||||
// - the list of supplied options (with their respective values, when
|
||||
// applicable);
|
||||
// - the list of non-option arguments that were given after the options.
|
||||
//
|
||||
// Both of these lists (vectors) may be empty and preserve the order used in
|
||||
// 'argv'.
|
||||
std::pair< std::vector<OptionValue>, std::vector<std::string> >
|
||||
parseArgs(int argc, const char *const *argv) const;
|
||||
|
||||
private:
|
||||
// Convert from the encoding used for argv (command line arguments) to
|
||||
// UTF-8.
|
||||
//
|
||||
// This method is currently not very satisfactory (cf. comments in the
|
||||
// implementation). The Windows code path is untested; the non-Windows code
|
||||
// path assumes command line arguments are encoded in UTF-8 (in other words,
|
||||
// it's a no-op).
|
||||
static std::string cmdEncToUtf8(const std::string& stringInCmdLineEncoding);
|
||||
|
||||
// Remove leading dashes and do sanity checks. 'longAliases' is modified
|
||||
// in-place. 'shortAliases' is not, because we build an std::vector<char>
|
||||
// from an std::vector<std::string>.
|
||||
static std::vector<char> removeHyphens(
|
||||
const std::vector<std::string>& shortAliases,
|
||||
std::vector<std::string>& longAliases);
|
||||
|
||||
// Read a long option and its value, if any (in total: one or two command
|
||||
// line arguments).
|
||||
//
|
||||
// Return the number of arguments consumed by this process after
|
||||
// 'currentArg' (i.e., 0 or 1 depending on whether the last option in
|
||||
// 'currentArg' has been given a value).
|
||||
//
|
||||
// 'currentArg' comes from argv[nextArgIdx-1], after decoding by
|
||||
// cmdEncToUtf8(). Thus, argv[nextArgIdx] is the command-line argument
|
||||
// coming after 'currentArg'.
|
||||
int readLongOption(
|
||||
int argc, const char *const *argv, const std::string& currentArg,
|
||||
int nextArgIdx, std::vector<OptionValue>& optsWithValues) const;
|
||||
// Read all short options in a command line argument, plus the option value
|
||||
// of the last one of these, if any (even if the option value is in the next
|
||||
// command line argument).
|
||||
//
|
||||
// See readLongOption() for the return value and meaning of parameters.
|
||||
int readShortOptions(
|
||||
int argc, const char *const *argv, const std::string& currentArg,
|
||||
int nextArgIdx, std::vector<OptionValue>& optsWithValues) const;
|
||||
|
||||
// Keys are short option names without the leading dash
|
||||
std::unordered_map< char,
|
||||
std::shared_ptr<const OptionDesc> > _shortOptionMap;
|
||||
// Keys are long option names without the two leading dashes
|
||||
std::unordered_map< std::string,
|
||||
std::shared_ptr<const OptionDesc> > _longOptionMap;
|
||||
};
|
||||
|
||||
} // of namespace argparse
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // _SIMGEAR_ARGPARSE_HXX_
|
||||
621
simgear/misc/argparse_test.cxx
Normal file
621
simgear/misc/argparse_test.cxx
Normal file
@@ -0,0 +1,621 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// argparse_test.cxx --- Automated tests for argparse.cxx / argparse.hxx
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <iostream> // std::cout
|
||||
#include <vector>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include "argparse.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
void test_mixOfShortAndLongOptions()
|
||||
{
|
||||
cout << "Testing a mix of short and long options, plus non-option arguments"
|
||||
<< endl;
|
||||
|
||||
using namespace simgear::argparse;
|
||||
ArgumentParser parser;
|
||||
|
||||
parser.addOption("test option", OptionArgType::NO_ARGUMENT, "-t");
|
||||
parser.addOption("long opt w/o arg", OptionArgType::NO_ARGUMENT,
|
||||
"", "--long-option-without-arg");
|
||||
parser.addOption("other test option", OptionArgType::NO_ARGUMENT, "-O");
|
||||
parser.addOption("yet another test option",
|
||||
OptionArgType::OPTIONAL_ARGUMENT, "-y", "--yes-we-can");
|
||||
parser.addOption("and again", OptionArgType::MANDATORY_ARGUMENT, "-a",
|
||||
"--all-you-need-is-love");
|
||||
parser.addOption("long option with opt arg",
|
||||
OptionArgType::OPTIONAL_ARGUMENT, "", // no short alias
|
||||
"--long-option-with-opt-arg");
|
||||
|
||||
// Using an std::vector to avoid the need to count the elements ourselves
|
||||
const vector<const char*> v({
|
||||
"FoobarProg", "-Oy", "-aarg for -a", "-OtOty", "arg for -y",
|
||||
"-tyOther arg for -y", "--long-option-without-arg", "-a", "Arg for -a",
|
||||
"--long-option-with-opt-arg", "--long-option-with-opt-arg", "value 1",
|
||||
"--long-option-with-opt-arg=value 2", "-t",
|
||||
"--all-you-need-is-love", "oh this is true", "-ypouet",
|
||||
"--all-you-need-is-love=right, I'll shut up ;-)", "non option",
|
||||
" other non option ", "--", "<-- came too late, treated as an arg"});
|
||||
// v.size() corresponds to argc, &v[0] corresponds to argv.
|
||||
const auto res = parser.parseArgs(v.size(), &v[0]);
|
||||
const auto& opts = res.first;
|
||||
const auto& otherArgs = res.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts.size(), 19); // number of passed options
|
||||
SG_CHECK_EQUAL(otherArgs.size(), 4); // number of non-option arguments
|
||||
|
||||
// Check all passed options and their values
|
||||
SG_CHECK_EQUAL(opts[0].passedAs(), "-O");
|
||||
SG_CHECK_EQUAL(opts[0].value(), "");
|
||||
SG_CHECK_EQUAL(opts[0].hasValue(), false);
|
||||
|
||||
SG_CHECK_EQUAL(opts[0].id(), "other test option");
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[0].optionDesc()->argumentType(),
|
||||
OptionArgType::NO_ARGUMENT);
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[0].optionDesc()->shortAliases(),
|
||||
vector<char>(1, 'O'));
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[0].optionDesc()->longAliases(), vector<string>());
|
||||
|
||||
SG_CHECK_EQUAL(opts[1].passedAs(), "-y");
|
||||
SG_CHECK_EQUAL(opts[1].value(), "");
|
||||
SG_CHECK_EQUAL(opts[1].hasValue(), false);
|
||||
|
||||
SG_CHECK_EQUAL(opts[1].id(), "yet another test option");
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[1].optionDesc()->argumentType(),
|
||||
OptionArgType::OPTIONAL_ARGUMENT);
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[1].optionDesc()->shortAliases(),
|
||||
vector<char>(1, 'y'));
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[1].optionDesc()->longAliases(),
|
||||
vector<string>(1, "yes-we-can"));
|
||||
|
||||
SG_CHECK_EQUAL(opts[2].passedAs(), "-a");
|
||||
SG_CHECK_EQUAL(opts[2].value(), "arg for -a");
|
||||
SG_CHECK_EQUAL(opts[2].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[2].id(), "and again");
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[2].optionDesc()->argumentType(),
|
||||
OptionArgType::MANDATORY_ARGUMENT);
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[2].optionDesc()->shortAliases(),
|
||||
vector<char>(1, 'a'));
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[2].optionDesc()->longAliases(),
|
||||
vector<string>(1, "all-you-need-is-love"));
|
||||
|
||||
SG_CHECK_EQUAL(opts[3].passedAs(), "-O");
|
||||
SG_CHECK_EQUAL(opts[3].value(), "");
|
||||
SG_CHECK_EQUAL(opts[3].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[3].id(), "other test option");
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[3].optionDesc()->argumentType(),
|
||||
OptionArgType::NO_ARGUMENT);
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[3].optionDesc()->shortAliases(),
|
||||
vector<char>(1, 'O'));
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[3].optionDesc()->longAliases(), vector<string>());
|
||||
|
||||
SG_CHECK_EQUAL(opts[4].passedAs(), "-t");
|
||||
SG_CHECK_EQUAL(opts[4].value(), "");
|
||||
SG_CHECK_EQUAL(opts[4].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[4].id(), "test option");
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[4].optionDesc()->argumentType(),
|
||||
OptionArgType::NO_ARGUMENT);
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[4].optionDesc()->shortAliases(),
|
||||
vector<char>(1, 't'));
|
||||
SG_CHECK_EQUAL_NOSTREAM(opts[4].optionDesc()->longAliases(), vector<string>());
|
||||
|
||||
SG_CHECK_EQUAL(opts[5].passedAs(), "-O");
|
||||
SG_CHECK_EQUAL(opts[5].value(), "");
|
||||
SG_CHECK_EQUAL(opts[5].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[5].id(), "other test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[6].passedAs(), "-t");
|
||||
SG_CHECK_EQUAL(opts[6].value(), "");
|
||||
SG_CHECK_EQUAL(opts[6].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[6].id(), "test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[7].passedAs(), "-y");
|
||||
SG_CHECK_EQUAL(opts[7].value(), "arg for -y");
|
||||
SG_CHECK_EQUAL(opts[7].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[7].id(), "yet another test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[8].passedAs(), "-t");
|
||||
SG_CHECK_EQUAL(opts[8].value(), "");
|
||||
SG_CHECK_EQUAL(opts[8].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[8].id(), "test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[9].passedAs(), "-y");
|
||||
SG_CHECK_EQUAL(opts[9].value(), "Other arg for -y");
|
||||
SG_CHECK_EQUAL(opts[9].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[9].id(), "yet another test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[10].passedAs(), "--long-option-without-arg");
|
||||
SG_CHECK_EQUAL(opts[10].value(), "");
|
||||
SG_CHECK_EQUAL(opts[10].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[10].id(), "long opt w/o arg");
|
||||
|
||||
SG_CHECK_EQUAL(opts[11].passedAs(), "-a");
|
||||
SG_CHECK_EQUAL(opts[11].value(), "Arg for -a");
|
||||
SG_CHECK_EQUAL(opts[11].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[11].id(), "and again");
|
||||
|
||||
SG_CHECK_EQUAL(opts[12].passedAs(), "--long-option-with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[12].value(), "");
|
||||
SG_CHECK_EQUAL(opts[12].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[12].id(), "long option with opt arg");
|
||||
|
||||
SG_CHECK_EQUAL(opts[13].passedAs(), "--long-option-with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[13].value(), "value 1");
|
||||
SG_CHECK_EQUAL(opts[13].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[13].id(), "long option with opt arg");
|
||||
|
||||
SG_CHECK_EQUAL(opts[14].passedAs(), "--long-option-with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[14].value(), "value 2");
|
||||
SG_CHECK_EQUAL(opts[14].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[14].id(), "long option with opt arg");
|
||||
|
||||
SG_CHECK_EQUAL(opts[15].passedAs(), "-t");
|
||||
SG_CHECK_EQUAL(opts[15].value(), "");
|
||||
SG_CHECK_EQUAL(opts[15].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[15].id(), "test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[16].passedAs(), "--all-you-need-is-love");
|
||||
SG_CHECK_EQUAL(opts[16].value(), "oh this is true");
|
||||
SG_CHECK_EQUAL(opts[16].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[16].id(), "and again");
|
||||
|
||||
SG_CHECK_EQUAL(opts[17].passedAs(), "-y");
|
||||
SG_CHECK_EQUAL(opts[17].value(), "pouet");
|
||||
SG_CHECK_EQUAL(opts[17].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[17].id(), "yet another test option");
|
||||
|
||||
SG_CHECK_EQUAL(opts[18].passedAs(), "--all-you-need-is-love");
|
||||
SG_CHECK_EQUAL(opts[18].value(), "right, I'll shut up ;-)");
|
||||
SG_CHECK_EQUAL(opts[18].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[18].id(), "and again");
|
||||
|
||||
// Check all non-option arguments that were passed to parser.parseArgs()
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs,
|
||||
vector<string>({"non option", " other non option ", "--",
|
||||
"<-- came too late, treated as an arg"}));
|
||||
}
|
||||
|
||||
void test_whenOptionValueIsASingleHyphen()
|
||||
{
|
||||
cout << "Testing cases where a single hyphen is used as an option value" <<
|
||||
endl;
|
||||
|
||||
using namespace simgear::argparse;
|
||||
ArgumentParser parser;
|
||||
|
||||
parser.addOption("option -T", OptionArgType::NO_ARGUMENT, "-T", "--test");
|
||||
parser.addOption("option -o", OptionArgType::OPTIONAL_ARGUMENT,
|
||||
"-o", "--with-opt-arg");
|
||||
parser.addOption("option -m", OptionArgType::MANDATORY_ARGUMENT, "-m",
|
||||
"--with-mandatory-arg");
|
||||
|
||||
const vector<const char*> v({
|
||||
"FoobarProg", "-To", "-", "-o-", "-oT", "-o", "-T", "-o", "-",
|
||||
"--with-opt-arg=-", "--with-opt-arg", "-", "--with-opt-arg",
|
||||
"-m-", "--with-mandatory-arg=-", "--with-mandatory-arg", "-", "-m", "-",
|
||||
"non option 1", "non option 2", "non option 3"});
|
||||
const auto res = parser.parseArgs(v.size(), &v[0]);
|
||||
const auto& opts = res.first;
|
||||
const auto& otherArgs = res.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts.size(), 14); // number of passed options
|
||||
SG_CHECK_EQUAL(otherArgs.size(), 3); // number of non-option arguments
|
||||
|
||||
SG_CHECK_EQUAL(opts[0].passedAs(), "-T");
|
||||
SG_CHECK_EQUAL(opts[0].value(), "");
|
||||
SG_CHECK_EQUAL(opts[0].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[0].id(), "option -T");
|
||||
|
||||
SG_CHECK_EQUAL(opts[1].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[1].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[1].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[1].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[2].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[2].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[2].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[2].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[3].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[3].value(), "T");
|
||||
SG_CHECK_EQUAL(opts[3].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[3].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[4].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[4].value(), "");
|
||||
SG_CHECK_EQUAL(opts[4].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[4].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[5].passedAs(), "-T");
|
||||
SG_CHECK_EQUAL(opts[5].value(), "");
|
||||
SG_CHECK_EQUAL(opts[5].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[5].id(), "option -T");
|
||||
|
||||
SG_CHECK_EQUAL(opts[6].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[6].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[6].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[6].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[7].passedAs(), "--with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[7].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[7].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[7].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[8].passedAs(), "--with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[8].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[8].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[8].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[9].passedAs(), "--with-opt-arg");
|
||||
SG_CHECK_EQUAL(opts[9].value(), "");
|
||||
SG_CHECK_EQUAL(opts[9].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[9].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[10].passedAs(), "-m");
|
||||
SG_CHECK_EQUAL(opts[10].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[10].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[10].id(), "option -m");
|
||||
|
||||
SG_CHECK_EQUAL(opts[11].passedAs(), "--with-mandatory-arg");
|
||||
SG_CHECK_EQUAL(opts[11].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[11].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[11].id(), "option -m");
|
||||
|
||||
SG_CHECK_EQUAL(opts[12].passedAs(), "--with-mandatory-arg");
|
||||
SG_CHECK_EQUAL(opts[12].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[12].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[12].id(), "option -m");
|
||||
|
||||
SG_CHECK_EQUAL(opts[13].passedAs(), "-m");
|
||||
SG_CHECK_EQUAL(opts[13].value(), "-");
|
||||
SG_CHECK_EQUAL(opts[13].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[13].id(), "option -m");
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs,
|
||||
vector<string>({"non option 1", "non option 2", "non option 3"}));
|
||||
}
|
||||
|
||||
void test_frontierBetweenOptionsAndNonOptions()
|
||||
{
|
||||
cout << "Testing around the frontier between options and non-options" << endl;
|
||||
|
||||
using namespace simgear::argparse;
|
||||
ArgumentParser parser;
|
||||
|
||||
parser.addOption("option -T", OptionArgType::NO_ARGUMENT, "-T");
|
||||
parser.addOption("long opt w/o arg", OptionArgType::NO_ARGUMENT,
|
||||
"", "--long-option-without-arg");
|
||||
parser.addOption("option -a", OptionArgType::MANDATORY_ARGUMENT, "-a",
|
||||
"--this-is-option-a");
|
||||
|
||||
// Test 1: both options and non-options; '--' used as a normal non-option
|
||||
// argument (i.e., after other non-option arguments).
|
||||
const vector<const char*> v1({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval", "non option 1",
|
||||
"non option 2", "--", "non option 3"});
|
||||
// v1.size() corresponds to argc, &v1[0] corresponds to argv.
|
||||
const auto res1 = parser.parseArgs(v1.size(), &v1[0]);
|
||||
const auto& opts1 = res1.first;
|
||||
const auto& otherArgs1 = res1.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts1.size(), 2); // number of passed options
|
||||
SG_CHECK_EQUAL(otherArgs1.size(), 4); // number of non-option arguments
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs1,
|
||||
vector<string>({"non option 1", "non option 2", "--", "non option 3"}));
|
||||
|
||||
// Test 2: some options but no non-options arguments
|
||||
const vector<const char*> v2({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval"});
|
||||
const auto res2 = parser.parseArgs(v2.size(), &v2[0]);
|
||||
const auto& opts2 = res2.first;
|
||||
const auto& otherArgs2 = res2.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts2.size(), 2);
|
||||
SG_VERIFY(otherArgs2.empty());
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(otherArgs2, vector<string>());
|
||||
|
||||
// Test 3: same as test 2, but with useless end-of-options delimiter
|
||||
const vector<const char*> v3({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval", "--"});
|
||||
const auto res3 = parser.parseArgs(v3.size(), &v3[0]);
|
||||
const auto& opts3 = res3.first;
|
||||
const auto& otherArgs3 = res3.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts3.size(), 2);
|
||||
SG_VERIFY(otherArgs3.empty());
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(otherArgs3, vector<string>());
|
||||
|
||||
// Test 4: only non-option arguments
|
||||
const vector<const char*> v4({
|
||||
"FoobarProg", "non option 1",
|
||||
"non option 2", "--", "non option 3"});
|
||||
const auto res4 = parser.parseArgs(v4.size(), &v4[0]);
|
||||
const auto& opts4 = res4.first;
|
||||
const auto& otherArgs4 = res4.second;
|
||||
|
||||
SG_VERIFY(opts4.empty());
|
||||
SG_CHECK_EQUAL(otherArgs4.size(), 4);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs4,
|
||||
vector<string>({"non option 1", "non option 2", "--", "non option 3"}));
|
||||
|
||||
// Test 5: only non-options arguments, but starting with --
|
||||
const vector<const char*> v5({
|
||||
"FoobarProg", "--", "non option 1",
|
||||
"non option 2", "--", "non option 3"});
|
||||
const auto res5 = parser.parseArgs(v5.size(), &v5[0]);
|
||||
const auto& opts5 = res5.first;
|
||||
const auto& otherArgs5 = res5.second;
|
||||
|
||||
SG_VERIFY(opts5.empty());
|
||||
SG_CHECK_EQUAL(otherArgs5.size(), 4);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs5,
|
||||
vector<string>({"non option 1", "non option 2", "--", "non option 3"}));
|
||||
|
||||
// Test 6: use the '--' delimiter before what would otherwise be considered
|
||||
// an option
|
||||
const vector<const char*> v6({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval", "--", "-T",
|
||||
"non option 1", "non option 2", "--", "non option 3"});
|
||||
const auto res6 = parser.parseArgs(v6.size(), &v6[0]);
|
||||
const auto& opts6 = res6.first;
|
||||
const auto& otherArgs6 = res6.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts6.size(), 2);
|
||||
SG_CHECK_EQUAL(otherArgs6.size(), 5);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs6,
|
||||
vector<string>({"-T", "non option 1", "non option 2", "--",
|
||||
"non option 3"}));
|
||||
|
||||
// Test 7: use the '--' delimiter before an argument that doesn't look like
|
||||
// an option
|
||||
const vector<const char*> v7({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval", "--",
|
||||
"doesn't look like an option", "non option 1", "non option 2", "--",
|
||||
"non option 3"});
|
||||
const auto res7 = parser.parseArgs(v7.size(), &v7[0]);
|
||||
const auto& opts7 = res7.first;
|
||||
const auto& otherArgs7 = res7.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts7.size(), 2);
|
||||
SG_CHECK_EQUAL(otherArgs7.size(), 5);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs7,
|
||||
vector<string>({"doesn't look like an option", "non option 1",
|
||||
"non option 2", "--", "non option 3"}));
|
||||
|
||||
// Test 8: the argument marking the end of options is the empty string
|
||||
const vector<const char*> v8({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval",
|
||||
"", "non option 1", "non option 2", "-", "non option 3"});
|
||||
const auto res8 = parser.parseArgs(v8.size(), &v8[0]);
|
||||
const auto& opts8 = res8.first;
|
||||
const auto& otherArgs8 = res8.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts8.size(), 2);
|
||||
SG_CHECK_EQUAL(otherArgs8.size(), 5);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs8,
|
||||
vector<string>({"", "non option 1", "non option 2", "-", "non option 3"}));
|
||||
|
||||
// Test 9: the argument marking the end of options is a single hyphen
|
||||
const vector<const char*> v9({
|
||||
"FoobarProg", "--long-option-without-arg", "-aval",
|
||||
"-", "non option 1", "non option 2", "-", "non option 3"});
|
||||
const auto res9 = parser.parseArgs(v9.size(), &v9[0]);
|
||||
const auto& opts9 = res9.first;
|
||||
const auto& otherArgs9 = res9.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts9.size(), 2);
|
||||
SG_CHECK_EQUAL(otherArgs9.size(), 5);
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs9,
|
||||
vector<string>({"-", "non option 1", "non option 2", "-", "non option 3"}));
|
||||
|
||||
// Test 10: no other argument than the program name in argv
|
||||
const vector<const char*> v10({"FoobarProg"});
|
||||
const auto res10 = parser.parseArgs(v10.size(), &v10[0]);
|
||||
const auto& opts10 = res10.first;
|
||||
const auto& otherArgs10 = res10.second;
|
||||
|
||||
SG_VERIFY(opts10.empty());
|
||||
SG_VERIFY(otherArgs10.empty());
|
||||
}
|
||||
|
||||
void test_optionsWithMultipleAliases()
|
||||
{
|
||||
cout << "Testing options with multiple aliases" << endl;
|
||||
|
||||
using namespace simgear::argparse;
|
||||
ArgumentParser parser;
|
||||
|
||||
parser.addOption("option -o", OptionArgType::OPTIONAL_ARGUMENT,
|
||||
vector<string>({"-o", "-O", "-0"}),
|
||||
vector<string>({"--o-alias-1", "--o-alias-2"}));
|
||||
parser.addOption("option -a", OptionArgType::MANDATORY_ARGUMENT,
|
||||
vector<string>({"-a", "-r"}),
|
||||
vector<string>({"--a-alias-1", "--a-alias-2",
|
||||
"--a-alias-3"}));
|
||||
parser.addOption("option -N", OptionArgType::NO_ARGUMENT,
|
||||
vector<string>({"-N", "-p"}),
|
||||
vector<string>({"--N-alias-1", "--N-alias-2"}));
|
||||
|
||||
const vector<const char*> v({
|
||||
"FoobarProg", "--o-alias-1", "-aarg for -a", "-pO", "arg for -O",
|
||||
"--a-alias-2=value 1", "--o-alias-2", "value 2", "-Novalue 3",
|
||||
"--N-alias-2", "--a-alias-3=value 4", "-0value 5", "--N-alias-1",
|
||||
"non option 1", "non option 2", "non option 3"});
|
||||
// v.size() corresponds to argc, &v[0] corresponds to argv.
|
||||
const auto res = parser.parseArgs(v.size(), &v[0]);
|
||||
const auto& opts = res.first;
|
||||
const auto& otherArgs = res.second;
|
||||
|
||||
SG_CHECK_EQUAL(opts.size(), 12); // number of passed options
|
||||
SG_CHECK_EQUAL(otherArgs.size(), 3); // number of non-option arguments
|
||||
|
||||
SG_CHECK_EQUAL(opts[0].passedAs(), "--o-alias-1");
|
||||
SG_CHECK_EQUAL(opts[0].value(), "");
|
||||
SG_CHECK_EQUAL(opts[0].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[0].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[1].passedAs(), "-a");
|
||||
SG_CHECK_EQUAL(opts[1].value(), "arg for -a");
|
||||
SG_CHECK_EQUAL(opts[1].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[1].id(), "option -a");
|
||||
|
||||
SG_CHECK_EQUAL(opts[2].passedAs(), "-p");
|
||||
SG_CHECK_EQUAL(opts[2].value(), "");
|
||||
SG_CHECK_EQUAL(opts[2].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[2].id(), "option -N");
|
||||
|
||||
SG_CHECK_EQUAL(opts[3].passedAs(), "-O");
|
||||
SG_CHECK_EQUAL(opts[3].value(), "arg for -O");
|
||||
SG_CHECK_EQUAL(opts[3].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[3].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[4].passedAs(), "--a-alias-2");
|
||||
SG_CHECK_EQUAL(opts[4].value(), "value 1");
|
||||
SG_CHECK_EQUAL(opts[4].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[4].id(), "option -a");
|
||||
|
||||
SG_CHECK_EQUAL(opts[5].passedAs(), "--o-alias-2");
|
||||
SG_CHECK_EQUAL(opts[5].value(), "value 2");
|
||||
SG_CHECK_EQUAL(opts[5].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[5].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[6].passedAs(), "-N");
|
||||
SG_CHECK_EQUAL(opts[6].value(), "");
|
||||
SG_CHECK_EQUAL(opts[6].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[6].id(), "option -N");
|
||||
|
||||
SG_CHECK_EQUAL(opts[7].passedAs(), "-o");
|
||||
SG_CHECK_EQUAL(opts[7].value(), "value 3");
|
||||
SG_CHECK_EQUAL(opts[7].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[7].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[8].passedAs(), "--N-alias-2");
|
||||
SG_CHECK_EQUAL(opts[8].value(), "");
|
||||
SG_CHECK_EQUAL(opts[8].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[8].id(), "option -N");
|
||||
|
||||
SG_CHECK_EQUAL(opts[9].passedAs(), "--a-alias-3");
|
||||
SG_CHECK_EQUAL(opts[9].value(), "value 4");
|
||||
SG_CHECK_EQUAL(opts[9].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[9].id(), "option -a");
|
||||
|
||||
SG_CHECK_EQUAL(opts[10].passedAs(), "-0");
|
||||
SG_CHECK_EQUAL(opts[10].value(), "value 5");
|
||||
SG_CHECK_EQUAL(opts[10].hasValue(), true);
|
||||
SG_CHECK_EQUAL(opts[10].id(), "option -o");
|
||||
|
||||
SG_CHECK_EQUAL(opts[11].passedAs(), "--N-alias-1");
|
||||
SG_CHECK_EQUAL(opts[11].value(), "");
|
||||
SG_CHECK_EQUAL(opts[11].hasValue(), false);
|
||||
SG_CHECK_EQUAL(opts[11].id(), "option -N");
|
||||
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
otherArgs,
|
||||
vector<string>({"non option 1", "non option 2", "non option 3"}));
|
||||
}
|
||||
|
||||
// Auxiliary function used by test_invalidOptionOrArgumentMissing()
|
||||
void aux_invalidOptionOrMissingArgument_checkRaiseExcecption(
|
||||
const simgear::argparse::ArgumentParser& parser,
|
||||
const vector<const char*>& v)
|
||||
{
|
||||
bool gotException = false;
|
||||
|
||||
try {
|
||||
parser.parseArgs(v.size(), &v[0]);
|
||||
} catch (const simgear::argparse::Error&) {
|
||||
gotException = true;
|
||||
}
|
||||
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
void test_invalidOptionOrMissingArgument()
|
||||
{
|
||||
cout << "Testing passing invalid options and other syntax errors" << endl;
|
||||
|
||||
using simgear::argparse::OptionArgType;
|
||||
|
||||
simgear::argparse::ArgumentParser parser;
|
||||
parser.addOption("option -o", OptionArgType::OPTIONAL_ARGUMENT, "-o");
|
||||
parser.addOption("option -m", OptionArgType::MANDATORY_ARGUMENT,
|
||||
"-m", "--mandatory-arg");
|
||||
parser.addOption("option -n", OptionArgType::NO_ARGUMENT, "-n", "--no-arg");
|
||||
|
||||
const vector<vector<const char*> > listOfArgvs({
|
||||
{"FoobarProg", "-ovalue", "-n", "-X",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-nXn",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-n", "--non-existent-option",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-n", "--non-existent-option=value",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-n", "-m", "--",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-n", "-X", "-m"},
|
||||
{"FoobarProg", "-ovalue", "-n", "--mandatory-arg", "--",
|
||||
"non option 1", "non option 2", "non option 3"},
|
||||
{"FoobarProg", "-ovalue", "-n", "--mandatory-arg"}
|
||||
});
|
||||
|
||||
for (const auto& argv: listOfArgvs) {
|
||||
aux_invalidOptionOrMissingArgument_checkRaiseExcecption(parser, argv);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *const *argv)
|
||||
{
|
||||
test_mixOfShortAndLongOptions();
|
||||
test_whenOptionValueIsASingleHyphen();
|
||||
test_frontierBetweenOptionsAndNonOptions();
|
||||
test_optionsWithMultipleAliases();
|
||||
test_invalidOptionOrMissingArgument();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
@@ -53,9 +53,9 @@ static const char sgDirPathSep = '/';
|
||||
static const char sgDirPathSepBad = '\\';
|
||||
|
||||
#ifdef _WIN32
|
||||
const char SGPath::pathListSep = ';';
|
||||
const char SGPath::pathListSep[] = ";"; // this is null-terminated
|
||||
#else
|
||||
const char SGPath::pathListSep = ':';
|
||||
const char SGPath::pathListSep[] = ":"; // ditto
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -278,7 +278,6 @@ SGPath& SGPath::operator=(const SGPath& p)
|
||||
SGPath::~SGPath() {
|
||||
}
|
||||
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
// set path
|
||||
void SGPath::set( const string& p ) {
|
||||
path = p;
|
||||
@@ -286,7 +285,6 @@ void SGPath::set( const string& p ) {
|
||||
_cached = false;
|
||||
_rwCached = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::setPermissionChecker(PermissionChecker validator)
|
||||
@@ -334,7 +332,7 @@ SGPath SGPath::operator/( const std::string& p ) const
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
//add a new path component to the existing path string
|
||||
void SGPath::add( const string& p ) {
|
||||
append( SGPath::pathListSep+p );
|
||||
append( SGPath::pathListSep[0] + p );
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -534,10 +532,11 @@ void SGPath::validate() const
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::checkAccess() const
|
||||
{
|
||||
if( _rwCached && _cacheEnabled )
|
||||
if ( _rwCached && _cacheEnabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
validate();
|
||||
validate();
|
||||
_rwCached = true;
|
||||
}
|
||||
|
||||
@@ -666,7 +665,7 @@ string_list sgPathSplit( const string &search_path ) {
|
||||
bool done = false;
|
||||
|
||||
while ( !done ) {
|
||||
int index = tmp.find(SGPath::pathListSep);
|
||||
int index = tmp.find(SGPath::pathListSep[0]);
|
||||
if (index >= 0) {
|
||||
result.push_back( tmp.substr(0, index) );
|
||||
tmp = tmp.substr( index + 1 );
|
||||
|
||||
@@ -52,8 +52,8 @@ class SGPath {
|
||||
|
||||
public:
|
||||
|
||||
// OS-dependent separator used in paths lists
|
||||
static const char pathListSep;
|
||||
// OS-dependent separator used in paths lists (C-style string of length 1)
|
||||
static const char pathListSep[2];
|
||||
|
||||
struct Permissions
|
||||
{
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <ctype.h>
|
||||
#include <cstring>
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <string.h> // strerror_r() and strerror_s()
|
||||
#include <errno.h>
|
||||
#include <type_traits>
|
||||
#include <cstring> // strerror_r() and strerror_s()
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
|
||||
#if defined(HAVE_CPP11_CODECVT)
|
||||
#include <codecvt> // new in C++11
|
||||
@@ -372,6 +375,170 @@ namespace simgear {
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
int digitValue<10>(char c)
|
||||
{
|
||||
if ('0' <= c && c <= '9') {
|
||||
return static_cast<int>(c - '0');
|
||||
} else {
|
||||
throw sg_range_exception("invalid as a decimal digit: '" +
|
||||
std::string(1, c) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
int digitValue<16>(char c)
|
||||
{
|
||||
if ('0' <= c && c <= '9') {
|
||||
return static_cast<int>(c - '0');
|
||||
} else if ('a' <= c && c <= 'f') {
|
||||
return 10 + static_cast<int>(c - 'a');
|
||||
} else if ('A' <= c && c <= 'F') {
|
||||
return 10 + static_cast<int>(c - 'A');
|
||||
} else {
|
||||
throw sg_range_exception("invalid as an hexadecimal digit: '" +
|
||||
std::string(1, c) + "'");
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string numerationBaseAdjective<10>()
|
||||
{ return std::string("decimal"); }
|
||||
|
||||
template<>
|
||||
std::string numerationBaseAdjective<16>()
|
||||
{ return std::string("hexadecimal"); }
|
||||
|
||||
template<class T, int BASE, typename>
|
||||
T readNonNegativeInt(const std::string& s)
|
||||
{
|
||||
static_assert(0 < BASE,
|
||||
"template value BASE must be a positive integer");
|
||||
static_assert(BASE <= std::numeric_limits<T>::max(),
|
||||
"template type T too small: it cannot represent BASE");
|
||||
T res(0);
|
||||
T multiplier(1);
|
||||
T increment;
|
||||
int digit;
|
||||
|
||||
if (s.empty()) {
|
||||
throw sg_format_exception("expected a non-empty string", s);
|
||||
}
|
||||
|
||||
for (auto it = s.crbegin(); it != s.crend(); it++) {
|
||||
if (it != s.crbegin()) {
|
||||
// Check if 'multiplier *= BASE' is going to overflow. This is
|
||||
// reliable because 'multiplier' and 'BASE' are positive.
|
||||
if (multiplier > std::numeric_limits<T>::max() / BASE) {
|
||||
// If all remaining digits are '0', it doesn't matter that
|
||||
// the multiplier overflows.
|
||||
if (std::all_of(it, s.crend(),
|
||||
[](char c){ return (c == '0'); })) {
|
||||
return res;
|
||||
} else {
|
||||
throw sg_range_exception(
|
||||
"doesn't fit in the specified type: '" + s + "'");
|
||||
}
|
||||
}
|
||||
multiplier *= BASE;
|
||||
}
|
||||
|
||||
try {
|
||||
digit = digitValue<BASE>(*it);
|
||||
} catch (const sg_range_exception&) {
|
||||
throw sg_format_exception(
|
||||
"expected a string containing " +
|
||||
numerationBaseAdjective<BASE>() +
|
||||
" digits only, but got '" + s + "'", s);
|
||||
}
|
||||
|
||||
// Reliable because 'multiplier' is positive
|
||||
if (digit > 0 &&
|
||||
multiplier > std::numeric_limits<T>::max() / digit) {
|
||||
throw sg_range_exception(
|
||||
"doesn't fit in the specified type: '" + s + "'");
|
||||
}
|
||||
increment = multiplier*digit;
|
||||
|
||||
if (res > std::numeric_limits<T>::max() - increment) {
|
||||
throw sg_range_exception(
|
||||
"doesn't fit in the specified type: '" + s + "'");
|
||||
}
|
||||
res += increment;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Explicit template instantiations.
|
||||
//
|
||||
// In order to save some bytes for the SimGearCore library[*], we only
|
||||
// instantiate a small number of variants of readNonNegativeInt() below.
|
||||
// Just enable the ones you need if they are disabled.
|
||||
//
|
||||
// [*] The exact amount depends a lot on what you measure and in which
|
||||
// circumstances. On Linux amd64 with g++, I measured a cost ranging
|
||||
// from 2 KB per template in a Release build to 19 KB per template in
|
||||
// a RelWithDebInfo build for the in-memory code size of the resulting
|
||||
// fgfs binary (CODE column in 'top', after selecting a suitable
|
||||
// unit). If I look at the fgfs binary size (statically-linked with
|
||||
// SimGear), I measure from 2 KB per template (Release) to 30 KB per
|
||||
// template (RelWithDebInfo). Finally, a Debug build compiled with
|
||||
// '-fno-omit-frame-pointer -O0 -fno-inline' lies between the Release
|
||||
// and the RelWithDebInfo builds.
|
||||
|
||||
#if 0
|
||||
template
|
||||
signed char readNonNegativeInt<signed char, 10>(const std::string& s);
|
||||
template
|
||||
signed char readNonNegativeInt<signed char, 16>(const std::string& s);
|
||||
template
|
||||
unsigned char readNonNegativeInt<unsigned char, 10>(const std::string& s);
|
||||
template
|
||||
unsigned char readNonNegativeInt<unsigned char, 16>(const std::string& s);
|
||||
|
||||
template
|
||||
short readNonNegativeInt<short, 10>(const std::string& s);
|
||||
template
|
||||
short readNonNegativeInt<short, 16>(const std::string& s);
|
||||
template
|
||||
unsigned short readNonNegativeInt<unsigned short, 10>(const std::string& s);
|
||||
template
|
||||
unsigned short readNonNegativeInt<unsigned short, 16>(const std::string& s);
|
||||
#endif
|
||||
|
||||
template
|
||||
int readNonNegativeInt<int, 10>(const std::string& s);
|
||||
template
|
||||
unsigned int readNonNegativeInt<unsigned int, 10>(const std::string& s);
|
||||
|
||||
#if 0
|
||||
template
|
||||
int readNonNegativeInt<int, 16>(const std::string& s);
|
||||
template
|
||||
unsigned int readNonNegativeInt<unsigned int, 16>(const std::string& s);
|
||||
|
||||
template
|
||||
long readNonNegativeInt<long, 10>(const std::string& s);
|
||||
template
|
||||
long readNonNegativeInt<long, 16>(const std::string& s);
|
||||
template
|
||||
unsigned long readNonNegativeInt<unsigned long, 10>(const std::string& s);
|
||||
template
|
||||
unsigned long readNonNegativeInt<unsigned long, 16>(const std::string& s);
|
||||
|
||||
template
|
||||
long long readNonNegativeInt<long long, 10>(const std::string& s);
|
||||
template
|
||||
long long readNonNegativeInt<long long, 16>(const std::string& s);
|
||||
template
|
||||
unsigned long long readNonNegativeInt<unsigned long long, 10>(
|
||||
const std::string& s);
|
||||
template
|
||||
unsigned long long readNonNegativeInt<unsigned long long, 16>(
|
||||
const std::string& s);
|
||||
#endif
|
||||
|
||||
int compare_versions(const string& v1, const string& v2, int maxComponents)
|
||||
{
|
||||
vector<string> v1parts(split(v1, "."));
|
||||
@@ -630,6 +797,59 @@ std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
|
||||
return hex;
|
||||
}
|
||||
|
||||
// Write an octal backslash-escaped respresentation of 'val' to 'buf'.
|
||||
//
|
||||
// At least 4 write positions must be available at 'buf'. The result is *not*
|
||||
// null-terminated. Only the 8 least significant bits of 'val' are used;
|
||||
// higher-order bits have no influence on the chars written to 'buf'.
|
||||
static void writeOctalBackslashEscapedRepr(char *buf, unsigned char val)
|
||||
{
|
||||
buf[0] = '\\';
|
||||
buf[1] = '0' + ((val >> 6) & 3); // 2 bits
|
||||
buf[2] = '0' + ((val >> 3) & 7); // 3 bits
|
||||
buf[3] = '0' + (val & 7); // 3 bits
|
||||
}
|
||||
|
||||
// Backslash-escape a string for C/C++ string literal syntax.
|
||||
std::string escape(const std::string& s) {
|
||||
string res;
|
||||
char buf[4];
|
||||
|
||||
for (const char c: s) {
|
||||
// We don't really *need* to special-case \a, \b, \f, \n, \r, \t and \v,
|
||||
// because they could be handled like the other non-ASCII or non-printable
|
||||
// characters. However, doing so will make the output string both shorter
|
||||
// and more readable.
|
||||
if (c == '\a') {
|
||||
res += "\\a";
|
||||
} else if (c == '\b') {
|
||||
res += "\\b";
|
||||
} else if (c == '\f') {
|
||||
res += "\\f";
|
||||
} else if (c == '\n') {
|
||||
res += "\\n";
|
||||
} else if (c == '\r') {
|
||||
res += "\\r";
|
||||
} else if (c == '\t') {
|
||||
res += "\\t";
|
||||
} else if (c == '\v') {
|
||||
res += "\\v";
|
||||
} else if (c < 0x20 || c > 0x7e) { // non-ASCII or non-printable character
|
||||
// This is fast (no memory allocation nor IOStreams needed)
|
||||
writeOctalBackslashEscapedRepr(buf, static_cast<unsigned char>(c));
|
||||
res.append(buf, 4);
|
||||
} else if (c == '\\') {
|
||||
res += "\\\\";
|
||||
} else if (c == '"') {
|
||||
res += "\\\"";
|
||||
} else {
|
||||
res += c;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string unescape(const char* s)
|
||||
{
|
||||
@@ -665,16 +885,17 @@ std::string unescape(const char* s)
|
||||
if (!*++s)
|
||||
break;
|
||||
int v = 0;
|
||||
for (int i = 0; i < 2 && isxdigit(*s); i++, s++)
|
||||
for (/* empty */; isxdigit(*s); s++) {
|
||||
v = v * 16 + (isdigit(*s) ? *s - '0' : 10 + tolower(*s) - 'a');
|
||||
r += v;
|
||||
}
|
||||
r += static_cast<char>(v);
|
||||
continue;
|
||||
|
||||
} else if (*s >= '0' && *s <= '7') {
|
||||
int v = *s++ - '0';
|
||||
for (int i = 0; i < 3 && *s >= '0' && *s <= '7'; i++, s++)
|
||||
for (int i = 0; i < 2 && *s >= '0' && *s <= '7'; i++, s++)
|
||||
v = v * 8 + *s - '0';
|
||||
r += v;
|
||||
r += static_cast<char>(v);
|
||||
continue;
|
||||
|
||||
} else {
|
||||
@@ -739,6 +960,94 @@ std::string error_string(int errnum)
|
||||
#endif // !defined(_GNU_SOURCE)
|
||||
}
|
||||
|
||||
bool to_bool(const std::string& s)
|
||||
{
|
||||
if (!strcasecmp(s.c_str(), "yes")) return true;
|
||||
if (!strcasecmp(s.c_str(), "no")) return false;
|
||||
if (!strcasecmp(s.c_str(), "true")) return true;
|
||||
if (!strcasecmp(s.c_str(), "false")) return false;
|
||||
if (s == "1") return true;
|
||||
if (s == "0") return false;
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Unable to parse string as boolean:" << s);
|
||||
return false;
|
||||
}
|
||||
|
||||
enum PropMatchState
|
||||
{
|
||||
MATCH_LITERAL = 0,
|
||||
MATCH_WILD_INDEX,
|
||||
MATCH_WILD_NAME
|
||||
};
|
||||
|
||||
bool matchPropPathToTemplate(const std::string& path, const std::string& templatePath)
|
||||
{
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* pathPtr = path.c_str();
|
||||
const char* tPtr = templatePath.c_str();
|
||||
PropMatchState state = MATCH_LITERAL;
|
||||
|
||||
while (true) {
|
||||
bool advanceInTemplate = true;
|
||||
const char p = *pathPtr;
|
||||
if (p == 0) {
|
||||
// ran out of chars in the path. If we are matching a trailing
|
||||
// wildcard, this is a match, otherwise it's a fail
|
||||
if (state == MATCH_WILD_NAME) {
|
||||
// check this is the last * in the template string
|
||||
if (*(tPtr + 1) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case MATCH_LITERAL:
|
||||
if (*tPtr != p) {
|
||||
// literal mismatch
|
||||
return false;
|
||||
}
|
||||
++pathPtr;
|
||||
break;
|
||||
case MATCH_WILD_NAME:
|
||||
if ((p == '-') || isalpha(p)) {
|
||||
advanceInTemplate = false;
|
||||
++pathPtr;
|
||||
} else {
|
||||
// something else, we will advance in the template
|
||||
}
|
||||
break;
|
||||
case MATCH_WILD_INDEX:
|
||||
if (isdigit(p)) {
|
||||
advanceInTemplate = false;
|
||||
++pathPtr;
|
||||
} else {
|
||||
// something else, we will advance in the template
|
||||
}
|
||||
break;
|
||||
} // of state switch
|
||||
|
||||
if (advanceInTemplate) {
|
||||
const char nextTemplate = *(++tPtr);
|
||||
if (nextTemplate == 0) {
|
||||
// end of template, successful match
|
||||
return true;
|
||||
} else if (nextTemplate == '*') {
|
||||
state = (*(tPtr - 1) == '[') ? MATCH_WILD_INDEX : MATCH_WILD_NAME;
|
||||
} else {
|
||||
state = MATCH_LITERAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unreachable
|
||||
}
|
||||
|
||||
} // end namespace strutils
|
||||
|
||||
} // end namespace simgear
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <cstdlib>
|
||||
|
||||
typedef std::vector < std::string > string_list;
|
||||
@@ -168,7 +169,52 @@ namespace simgear {
|
||||
* convert a string representing a decimal number, to an int
|
||||
*/
|
||||
int to_int(const std::string& s, int base = 10);
|
||||
|
||||
|
||||
/** Convert a char to the integer it represents in the specified BASE.
|
||||
*
|
||||
* Contrary to std::isdigit() and std::isxdigit(), only the standard ASCII
|
||||
* digits for BASE are accepted (with both uppercase and lowercase 'a'-'f'
|
||||
* letters for base 16). Throw sg_range_exception if the char is not a
|
||||
* valid digit for this base.
|
||||
*
|
||||
* See template specializations in strutils.cxx.
|
||||
*/
|
||||
template<int BASE>
|
||||
int digitValue(char c);
|
||||
|
||||
/** Return:
|
||||
* - std::string("decimal") if BASE is 10;
|
||||
* - std::string("hexadecimal") if BASE is 16.
|
||||
*
|
||||
* Template specializations in strutils.cxx.
|
||||
*/
|
||||
template<int BASE>
|
||||
std::string numerationBaseAdjective();
|
||||
|
||||
/** Convert a string representing an integer to an integral type.
|
||||
*
|
||||
* The input string must be non-empty and contain only digits of the
|
||||
* specified BASE (template parameter). Throw:
|
||||
* - sg_format_exception if the input string doesn't respect these
|
||||
* constraints;
|
||||
* - sg_range_exception if the value can't be represented by type T
|
||||
* (i.e., if it is too large).
|
||||
*
|
||||
* Explicit template instantiations are added as needed in strutils.cxx.
|
||||
* Have a look there and enable the ones you need!
|
||||
*/
|
||||
template<
|
||||
class T,
|
||||
int BASE = 10,
|
||||
typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
|
||||
T readNonNegativeInt(const std::string& s);
|
||||
|
||||
/**
|
||||
* Convert a string representing a boolean, to a bool.
|
||||
* Accepted values include YES, true, 0, 1, false, no, True,
|
||||
*/
|
||||
bool to_bool(const std::string& s);
|
||||
|
||||
/**
|
||||
* Like strcmp(), but for dotted versions strings NN.NN.NN
|
||||
* any number of terms are supported.
|
||||
@@ -223,26 +269,43 @@ namespace simgear {
|
||||
* malformed
|
||||
*/
|
||||
void decodeBase64(const std::string& a, std::vector<unsigned char>& output);
|
||||
|
||||
|
||||
/**
|
||||
* convert bytes to hexadecimal equivalent
|
||||
*/
|
||||
std::string encodeHex(const std::string& bytes);
|
||||
|
||||
|
||||
std::string encodeHex(const unsigned char* rawBytes, unsigned int length);
|
||||
|
||||
/**
|
||||
* Backslash-escape a string for C/C++ string literal syntax.
|
||||
*
|
||||
* @param s Input string.
|
||||
* @return a copy of the input string with proper escaping, so that if the
|
||||
* result is part of a C or C++ file and enclosed in double
|
||||
* quotes, it can be used to represent a string literal that is
|
||||
* equal to the input string.
|
||||
*
|
||||
* @note For every std::string s: unescape(escape(s)) == s
|
||||
* @see unescape()
|
||||
*/
|
||||
std::string escape(const std::string& s);
|
||||
|
||||
/**
|
||||
* Unescape string.
|
||||
*
|
||||
* @param str String possibly containing escaped characters.
|
||||
* @return string with escaped characters replaced by single character
|
||||
* values.
|
||||
*
|
||||
* @note For every std::string s: unescape(escape(s)) == s
|
||||
* @see escape()
|
||||
*/
|
||||
std::string unescape(const char* str);
|
||||
|
||||
inline std::string unescape(const std::string& str)
|
||||
{ return unescape(str.c_str()); }
|
||||
|
||||
|
||||
/**
|
||||
* Check a printf-style format string for dangerous (buffer-overflowing,
|
||||
* memory re-writing) format tokens. If a problematic token is
|
||||
@@ -258,6 +321,20 @@ namespace simgear {
|
||||
*/
|
||||
std::string error_string(int errnum);
|
||||
|
||||
|
||||
/**
|
||||
* Match a property path, obtained from prop->getPath(), against a
|
||||
* template string. Templates are allowed to contain widlcards denoted by
|
||||
* an asterix in certain places - at the end of names, or inside indices.
|
||||
* Note that paths returned by getPath() always include an index on every
|
||||
* path component, so template strings should be structured accordingly.
|
||||
*
|
||||
* Examples:
|
||||
* /foo[*]/bar* will match /foo/barber, /foo[2]/bargain
|
||||
* /views[0]/view[*]/f* will match /views[0]/view[99]/foo,
|
||||
* /views[0]/view[4]/fig, /views[0]/view[1000]/flight
|
||||
*/
|
||||
bool matchPropPathToTemplate(const std::string& path, const std::string& templatePath);
|
||||
} // end namespace strutils
|
||||
} // end namespace simgear
|
||||
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// Unit tests for functions inside the strutils package
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h> // _set_errno() on Windows
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility> // std::move()
|
||||
#include <fstream> // std::ifstream
|
||||
#include <sstream> // std::ostringstream
|
||||
#include <ios> // std::dec, std::hex
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <typeinfo> // typeid()
|
||||
#include <cstdint> // uint16_t, uintmax_t, etc.
|
||||
#include <cstdlib> // _set_errno() on Windows
|
||||
#include <cerrno>
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace strutils = simgear::strutils;
|
||||
|
||||
@@ -85,6 +97,290 @@ void test_to_int()
|
||||
SG_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
|
||||
}
|
||||
|
||||
// Auxiliary function for test_readNonNegativeInt()
|
||||
void aux_readNonNegativeInt_setUpOStringStream(std::ostringstream& oss, int base)
|
||||
{
|
||||
switch (base) {
|
||||
case 10:
|
||||
oss << std::dec;
|
||||
break;
|
||||
case 16:
|
||||
oss << std::hex;
|
||||
break;
|
||||
default:
|
||||
SG_TEST_FAIL("unsupported value for 'base': " + std::to_string(base));
|
||||
}
|
||||
}
|
||||
|
||||
// Auxiliary function for test_readNonNegativeInt(): round-trip conversion for
|
||||
// the given number of values below and up to std::numeric_limits<T>::max().
|
||||
template<typename T, int BASE>
|
||||
void aux_readNonNegativeInt_testValuesCloseToMax(T nbValues)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
assert(0 <= nbValues && nbValues <= std::numeric_limits<T>::max());
|
||||
aux_readNonNegativeInt_setUpOStringStream(oss, BASE);
|
||||
|
||||
for (T i = std::numeric_limits<T>::max() - nbValues;
|
||||
i < std::numeric_limits<T>::max(); i++) {
|
||||
T valueToTest = i + 1;
|
||||
T roundTripResult;
|
||||
bool gotException = false;
|
||||
|
||||
oss.str("");
|
||||
// The cast is only useful when T is a char type
|
||||
oss << static_cast<uintmax_t>(valueToTest);
|
||||
|
||||
try {
|
||||
roundTripResult = strutils::readNonNegativeInt<T, BASE>(oss.str());
|
||||
} catch (const sg_range_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
|
||||
SG_VERIFY(!gotException);
|
||||
SG_CHECK_EQUAL(roundTripResult, valueToTest);
|
||||
}
|
||||
}
|
||||
|
||||
// Auxiliary class for test_readNonNegativeInt(): test that we do get an
|
||||
// exception when trying to convert the smallest, positive out-of-range value
|
||||
// for type T.
|
||||
template<typename T, int BASE>
|
||||
class ReadNonNegativeInt_JustOutOfRangeTester {
|
||||
public:
|
||||
ReadNonNegativeInt_JustOutOfRangeTester()
|
||||
{ }
|
||||
|
||||
// Run the test
|
||||
void run()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
aux_readNonNegativeInt_setUpOStringStream(oss, BASE);
|
||||
oss << 1 + static_cast<uintmax_t>(std::numeric_limits<T>::max());
|
||||
bool gotException = false;
|
||||
|
||||
try {
|
||||
strutils::readNonNegativeInt<T, BASE>(oss.str());
|
||||
} catch (const sg_range_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
};
|
||||
|
||||
class ReadNonNegativeInt_DummyTester {
|
||||
public:
|
||||
ReadNonNegativeInt_DummyTester()
|
||||
{ }
|
||||
|
||||
void run()
|
||||
{ }
|
||||
};
|
||||
|
||||
// We use this helper class to automatically determine for which types
|
||||
// ReadNonNegativeInt_JustOutOfRangeTester::run() can be run.
|
||||
template<typename T, int BASE>
|
||||
class AuxReadNonNegativeInt_JustOutOfRange_Helper
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type uT;
|
||||
|
||||
// Define TestRunner to be either
|
||||
//
|
||||
// ReadNonNegativeInt_JustOutOfRangeTester<T, BASE>
|
||||
//
|
||||
// or
|
||||
//
|
||||
// ReadNonNegativeInt_DummyTester
|
||||
//
|
||||
// depending on whether 1 + std::numeric_limits<T>::max() can be
|
||||
// represented by uintmax_t.
|
||||
typedef typename std::conditional<
|
||||
static_cast<uT>(std::numeric_limits<T>::max()) <
|
||||
std::numeric_limits<uintmax_t>::max(),
|
||||
ReadNonNegativeInt_JustOutOfRangeTester<T, BASE>,
|
||||
ReadNonNegativeInt_DummyTester >::type TestRunner;
|
||||
|
||||
public:
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper()
|
||||
{ };
|
||||
|
||||
void test()
|
||||
{
|
||||
TestRunner().run();
|
||||
}
|
||||
};
|
||||
|
||||
void test_readNonNegativeInt()
|
||||
{
|
||||
// In order to save some bytes for the SimGearCore library[*], we only
|
||||
// instantiated a small number of variants of readNonNegativeInt() in
|
||||
// strutils.cxx. This is why many tests are disabled with '#if 0' below. Of
|
||||
// course, more variants can be enabled when they are needed.
|
||||
//
|
||||
// [*] See measures in strutils.cxx before the template instantiations.
|
||||
|
||||
#if 0
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<short>("0")), 0);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<short>("23")), 23);
|
||||
#endif
|
||||
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int>("0")), 0);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int>("00000000")), 0);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int>("12345")), 12345);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int, 10>("12345")), 12345);
|
||||
|
||||
#if 0
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int, 16>("ff")), 0xff);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int, 16>("a5E9")), 0xa5e9);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<unsigned long, 16>("0cda")),
|
||||
0x0cda);
|
||||
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<uint16_t, 10>("65535")), 0xffff);
|
||||
SG_CHECK_EQUAL(
|
||||
(strutils::readNonNegativeInt<uint16_t, 10>("00000000000000000000065535")),
|
||||
0xffff);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<uint16_t, 16>("ffff")), 0xffff);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int16_t, 10>("32767")), 0x7fff);
|
||||
SG_CHECK_EQUAL((strutils::readNonNegativeInt<int16_t, 16>("7fff")), 0x7fff);
|
||||
#endif
|
||||
|
||||
// Nothing special about the values :)
|
||||
SG_CHECK_EQUAL_NOSTREAM((typeid(strutils::readNonNegativeInt<int, 10>("72"))),
|
||||
typeid(int(12)));
|
||||
#if 0
|
||||
SG_CHECK_EQUAL_NOSTREAM((typeid(strutils::readNonNegativeInt<long, 10>("72"))),
|
||||
typeid(12L));
|
||||
SG_CHECK_EQUAL_NOSTREAM(
|
||||
(typeid(strutils::readNonNegativeInt<long long, 10>("72"))),
|
||||
typeid(12LL));
|
||||
#endif
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int>(""); // empty string: illegal
|
||||
} catch (const sg_format_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int>("-1"); // non-digit character: illegal
|
||||
} catch (const sg_format_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int>("+1"); // non-digit character: illegal
|
||||
} catch (const sg_format_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int>("858efe"); // trailing garbage: illegal
|
||||
} catch (const sg_format_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int, 16>("858g5k"); // ditto for base 16
|
||||
} catch (const sg_format_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
strutils::readNonNegativeInt<int>(" 858"); // leading whitespace/garbage:
|
||||
} catch (const sg_format_exception&) { // illegal too
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
// Try to read a value that is 1 unit too large for the type. Check that it
|
||||
// raises an sg_range_exception in each case.
|
||||
#if 0
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<signed char, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<signed char, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned char, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned char, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<short, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<short, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned short, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned short, 16>().test();
|
||||
#endif
|
||||
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<int, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned int, 10>().test();
|
||||
|
||||
#if 0
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<int, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned int, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<long, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<long, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned long, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned long, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<long long, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<long long, 16>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned long long, 10>().test();
|
||||
AuxReadNonNegativeInt_JustOutOfRange_Helper<unsigned long long, 16>().test();
|
||||
#endif
|
||||
|
||||
// Round trip tests with large values, including the largest value that can
|
||||
// be represented by the type, in each case.
|
||||
//
|
||||
// Can be casted as any of the following types
|
||||
constexpr int nbValues = 5000;
|
||||
#if 0
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<signed char, 10>(127);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<signed char, 16>(127);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned char, 10>(127);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned char, 16>(127);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<short, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<short, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned short, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned short, 16>(nbValues);
|
||||
#endif
|
||||
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<int, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned int, 10>(nbValues);
|
||||
|
||||
#if 0
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<int, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned int, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<long, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<long, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned long, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned long, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<long long, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<long long, 16>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned long long, 10>(nbValues);
|
||||
aux_readNonNegativeInt_testValuesCloseToMax<unsigned long long, 16>(nbValues);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_split()
|
||||
{
|
||||
string_list l = strutils::split("zero one two three four five");
|
||||
@@ -139,9 +435,72 @@ void test_split()
|
||||
}
|
||||
}
|
||||
|
||||
void test_escape()
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::escape(""), "");
|
||||
SG_CHECK_EQUAL(strutils::escape("\\"), "\\\\");
|
||||
SG_CHECK_EQUAL(strutils::escape("\""), "\\\"");
|
||||
SG_CHECK_EQUAL(strutils::escape("\\n"), "\\\\n");
|
||||
SG_CHECK_EQUAL(strutils::escape("n\\"), "n\\\\");
|
||||
SG_CHECK_EQUAL(strutils::escape(" ab\nc \\def\t\r \\ ghi\\"),
|
||||
" ab\\nc \\\\def\\t\\r \\\\ ghi\\\\");
|
||||
// U+0152 is LATIN CAPITAL LIGATURE OE. The last word is Egg translated in
|
||||
// French and encoded in UTF-8 ('Œuf' if you can read UTF-8).
|
||||
SG_CHECK_EQUAL(strutils::escape("Un \"Bel\" '\u0152uf'"),
|
||||
"Un \\\"Bel\\\" '\\305\\222uf'");
|
||||
SG_CHECK_EQUAL(strutils::escape("\a\b\f\n\r\t\v"),
|
||||
"\\a\\b\\f\\n\\r\\t\\v");
|
||||
|
||||
// Test with non-printable characters
|
||||
//
|
||||
// - 'prefix' is an std::string that *contains* a NUL character.
|
||||
// - \012 is \n (LINE FEED).
|
||||
// - \037 (\x1F) is the last non-printable ASCII character before \040 (\x20),
|
||||
// which is the space.
|
||||
// - \176 (\x7E) is '~', the last printable ASCII character.
|
||||
// - \377 is \xFF. Higher char values (> 255) are not faithfully encoded by
|
||||
// strutils::escape(): only the lowest 8 bits are used; higher-order bits
|
||||
// are ignored (for people who use chars with more than 8 bits...).
|
||||
const string prefix = string("abc") + '\000';
|
||||
SG_CHECK_EQUAL(strutils::escape(prefix +
|
||||
"\003def\012\037\040\176\177\376\377"),
|
||||
"abc\\000\\003def\\n\\037 ~\\177\\376\\377");
|
||||
|
||||
SG_CHECK_EQUAL(strutils::escape(" \n\tAOa"), " \\n\\tAOa");
|
||||
}
|
||||
|
||||
void test_unescape()
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
// Two chars: '\033' (ESC) followed by '2'
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\0332"), "\0332");
|
||||
// Hex escapes have no length limit and terminate at the first character
|
||||
// that is not a valid hexadecimal digit.
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\x00020|"), " |");
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\xA"), "\n");
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\xA-"), "\n-");
|
||||
}
|
||||
|
||||
void aux_escapeAndUnescapeRoundTripTest(const string& testString)
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::unescape(strutils::escape(testString)), testString);
|
||||
}
|
||||
|
||||
void test_escapeAndUnescapeRoundTrips()
|
||||
{
|
||||
// "\0332" contains two chars: '\033' (ESC) followed by '2'.
|
||||
// Ditto for "\0402": it's a space ('\040') followed by a '2'.
|
||||
vector<string> stringsToTest(
|
||||
{"", "\\", "\n", "\\\\", "\"\'\?\t\rAG\v\a \b\f\\", "\x23\xf8",
|
||||
"\0332", "\0402", "\u00e0", "\U000000E9"});
|
||||
|
||||
const string withBinary = (string("abc") + '\000' +
|
||||
"\003def\012\037\040\176\177\376\377");
|
||||
stringsToTest.push_back(std::move(withBinary));
|
||||
|
||||
for (const string& s: stringsToTest) {
|
||||
aux_escapeAndUnescapeRoundTripTest(s);
|
||||
}
|
||||
}
|
||||
|
||||
void test_compare_versions()
|
||||
@@ -175,6 +534,32 @@ void test_md5_hex()
|
||||
SG_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
|
||||
}
|
||||
|
||||
void test_propPathMatch()
|
||||
{
|
||||
const char* testTemplate1 = "/sim[*]/views[*]/render";
|
||||
SG_VERIFY(strutils::matchPropPathToTemplate("/sim[0]/views[50]/render-buildings[0]", testTemplate1));
|
||||
SG_VERIFY(strutils::matchPropPathToTemplate("/sim[1]/views[0]/rendering-enabled", testTemplate1));
|
||||
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[0]/views[50]/something-else", testTemplate1));
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[0]/gui[0]/wibble", testTemplate1));
|
||||
|
||||
// test explicit index matching
|
||||
const char* testTemplate2 = "/view[5]/*";
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/view[2]/render-buildings[0]", testTemplate2));
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/sim[1]/foo", testTemplate2));
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/view[50]/foo", testTemplate2));
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/view[55]/foo", testTemplate2));
|
||||
|
||||
SG_VERIFY(strutils::matchPropPathToTemplate("/view[5]/foo", testTemplate2));
|
||||
SG_VERIFY(strutils::matchPropPathToTemplate("/view[5]/child[3]/bar", testTemplate2));
|
||||
|
||||
|
||||
const char* testTemplate3 = "/*[*]/fdm*[*]/aero*";
|
||||
|
||||
SG_VERIFY(strutils::matchPropPathToTemplate("/position[2]/fdm-jsb[0]/aerodynamic", testTemplate3));
|
||||
SG_VERIFY(!strutils::matchPropPathToTemplate("/position[2]/foo[0]/aerodynamic", testTemplate3));
|
||||
}
|
||||
|
||||
void test_error_string()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
@@ -205,11 +590,15 @@ int main(int argc, char* argv[])
|
||||
test_ends_with();
|
||||
test_simplify();
|
||||
test_to_int();
|
||||
test_readNonNegativeInt();
|
||||
test_split();
|
||||
test_escape();
|
||||
test_unescape();
|
||||
test_escapeAndUnescapeRoundTrips();
|
||||
test_compare_versions();
|
||||
test_md5_hex();
|
||||
test_error_string();
|
||||
test_propPathMatch();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -167,16 +167,14 @@ int parseTest()
|
||||
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");
|
||||
// thumbnails
|
||||
const pkg::Package::Thumbnail& thumb = p2->thumbnailForVariant(0);
|
||||
SG_CHECK_EQUAL(thumb.url, "http://foo.bar.com/thumb-exterior.png");
|
||||
SG_CHECK_EQUAL(thumb.path, "exterior.png");
|
||||
|
||||
// test variants
|
||||
SG_CHECK_EQUAL(p2->parentIdForVariant(0), std::string());
|
||||
|
||||
try {
|
||||
p2->indexOfVariant("fofofo");
|
||||
SG_TEST_FAIL("lookup of non-existant variant did not throw");
|
||||
@@ -190,6 +188,8 @@ int parseTest()
|
||||
unsigned int skisVariant = p2->indexOfVariant("c172p-skis");
|
||||
SG_VERIFY(skisVariant > 0);
|
||||
|
||||
SG_CHECK_EQUAL(p2->parentIdForVariant(skisVariantFull), "c172p");
|
||||
|
||||
SG_CHECK_EQUAL(skisVariant, skisVariantFull);
|
||||
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("description", skisVariant),
|
||||
@@ -199,6 +199,7 @@ int parseTest()
|
||||
|
||||
unsigned int floatsVariant = p2->indexOfVariant("c172p-floats");
|
||||
SG_VERIFY(floatsVariant > 0);
|
||||
SG_CHECK_EQUAL(p2->parentIdForVariant(floatsVariant), "c172p");
|
||||
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("description", floatsVariant),
|
||||
"A plane with floats");
|
||||
@@ -215,6 +216,22 @@ int parseTest()
|
||||
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
|
||||
SG_VERIFY(index->type == pkg::Package::Preview::Type::EXTERIOR);
|
||||
|
||||
const pkg::Package::Thumbnail& thumb2 = p2->thumbnailForVariant(floatsVariant);
|
||||
SG_CHECK_EQUAL(thumb2.url, "http://foo.bar.com/thumb-floats.png");
|
||||
SG_CHECK_EQUAL(thumb2.path, "thumb-floats.png");
|
||||
|
||||
// test multiple primary
|
||||
unsigned int rVariant = p2->indexOfVariant("c172r");
|
||||
SG_VERIFY(rVariant > 0);
|
||||
|
||||
SG_CHECK_EQUAL(p2->parentIdForVariant(rVariant), std::string());
|
||||
|
||||
unsigned int rFloatVariant = p2->indexOfVariant("c172r-floats");
|
||||
SG_VERIFY(rFloatVariant > 0);
|
||||
SG_CHECK_EQUAL(p2->parentIdForVariant(rFloatVariant), std::string("c172r"));
|
||||
|
||||
string_list primaries = {"c172p", "c172r"};
|
||||
SG_VERIFY(p2->primaryVariants() == primaries);
|
||||
|
||||
// test filtering / searching too
|
||||
string_set tags(p2->tags());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/package/Install.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -152,6 +153,11 @@ 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 */);
|
||||
@@ -450,33 +456,6 @@ void Install::cancelDownload()
|
||||
m_package->catalog()->root()->cancelDownload(this);
|
||||
}
|
||||
|
||||
struct PathAppender
|
||||
{
|
||||
PathAppender(const SGPath& p) : m_path(p) {}
|
||||
|
||||
SGPath operator()(const std::string& s) const
|
||||
{
|
||||
SGPath p(m_path);
|
||||
p.append(s);
|
||||
return p;
|
||||
}
|
||||
|
||||
SGPath m_path;
|
||||
};
|
||||
|
||||
PathList Install::thumbnailPaths() const
|
||||
{
|
||||
const string_list& thumbs(m_package->thumbnails());
|
||||
PathList result;
|
||||
if (thumbs.empty())
|
||||
return result;
|
||||
|
||||
std::transform(thumbs.begin(), thumbs.end(),
|
||||
std::back_inserter(result),
|
||||
PathAppender(m_path));
|
||||
return result;
|
||||
}
|
||||
|
||||
SGPath Install::primarySetPath() const
|
||||
{
|
||||
SGPath setPath(m_path);
|
||||
|
||||
@@ -98,13 +98,6 @@ public:
|
||||
*/
|
||||
void cancelDownload();
|
||||
|
||||
/**
|
||||
* return the thumbnails associated with this install, but as locations
|
||||
* on the file system, not URLs. It is assumed the order of thumbnails
|
||||
* is consistent with the URLs returned from Package::thumbnailUrls()
|
||||
*/
|
||||
PathList thumbnailPaths() const;
|
||||
|
||||
/**
|
||||
* Set the handler to be called when the installation successfully
|
||||
* completes.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
#include <cassert>
|
||||
@@ -274,28 +276,12 @@ SGPropertyNode* Package::properties() const
|
||||
|
||||
string_list Package::thumbnailUrls() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
string_list urls;
|
||||
const Thumbnail& thumb(thumbnailForVariant(0));
|
||||
if (!thumb.url.empty()) {
|
||||
urls.push_back(thumb.url);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
string_list Package::thumbnails() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
return urls;
|
||||
}
|
||||
|
||||
string_list Package::downloadUrls() const
|
||||
@@ -305,7 +291,7 @@ string_list Package::downloadUrls() const
|
||||
return r;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
|
||||
for (auto dl : m_props->getChildren("url")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
@@ -426,7 +412,49 @@ SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const cha
|
||||
return m_props;
|
||||
}
|
||||
|
||||
throw sg_exception("Unknow variant in package " + id());
|
||||
throw sg_exception("Unknown variant in package " + id());
|
||||
}
|
||||
|
||||
std::string Package::parentIdForVariant(unsigned int variantIndex) const
|
||||
{
|
||||
const std::string parentId = propsForVariant(variantIndex)->getStringValue("variant-of");
|
||||
if ((variantIndex == 0) || (parentId == "_package_")) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (parentId.empty()) {
|
||||
// this is a variant without a variant-of, so assume its parent is
|
||||
// the first primary
|
||||
return m_variants.front();
|
||||
}
|
||||
|
||||
assert(indexOfVariant(parentId) >= 0);
|
||||
return parentId;
|
||||
}
|
||||
|
||||
string_list Package::primaryVariants() const
|
||||
{
|
||||
string_list result;
|
||||
for (unsigned int v = 0; v < m_variants.size(); ++v) {
|
||||
const auto pr = parentIdForVariant(v);
|
||||
if (pr.empty()) {
|
||||
result.push_back(m_variants.at(v));
|
||||
}
|
||||
}
|
||||
assert(!result.empty());
|
||||
assert(result.front() == id());
|
||||
return result;
|
||||
}
|
||||
|
||||
Package::Thumbnail Package::thumbnailForVariant(unsigned int vIndex) const
|
||||
{
|
||||
SGPropertyNode_ptr var = propsForVariant(vIndex);
|
||||
// allow for variants without distinct thumbnails
|
||||
if (!var->hasChild("thumbnail") || !var->hasChild("thumbnail-path")) {
|
||||
var = m_props;
|
||||
}
|
||||
|
||||
return {var->getStringValue("thumbnail"), var->getStringValue("thumbnail-path")};
|
||||
}
|
||||
|
||||
Package::PreviewVec Package::previewsForVariant(unsigned int vIndex) const
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef std::set<std::string> string_set;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
|
||||
namespace pkg
|
||||
{
|
||||
|
||||
@@ -40,13 +40,13 @@ namespace pkg
|
||||
class Install;
|
||||
class Catalog;
|
||||
class Package;
|
||||
|
||||
|
||||
typedef SGSharedPtr<Package> PackageRef;
|
||||
typedef SGSharedPtr<Catalog> CatalogRef;
|
||||
typedef SGSharedPtr<Install> InstallRef;
|
||||
|
||||
typedef std::vector<PackageRef> PackageList;
|
||||
|
||||
|
||||
class Package : public SGReferenced
|
||||
{
|
||||
public:
|
||||
@@ -62,15 +62,25 @@ public:
|
||||
existingInstall(const InstallCallback& cb = InstallCallback()) const;
|
||||
|
||||
bool isInstalled() const;
|
||||
|
||||
|
||||
/**
|
||||
* package ID
|
||||
*/
|
||||
std::string id() const;
|
||||
|
||||
/**
|
||||
* Variant IDs. Note the primary ID will always be included as
|
||||
* variants()[0], to simplify enumerating all variants
|
||||
* Variant IDs
|
||||
*/
|
||||
string_list variants() const;
|
||||
|
||||
/**
|
||||
* All variants without a parent, i.e top-level variants in this package.
|
||||
* Often this is a single-element list matching id() above, but when
|
||||
* packages contain multiple primary aircraft, this will have multiple
|
||||
* elements.
|
||||
*/
|
||||
string_list primaryVariants() const;
|
||||
|
||||
/**
|
||||
* Fully-qualified ID, including our catalog'd ID
|
||||
*/
|
||||
@@ -89,8 +99,10 @@ public:
|
||||
/**
|
||||
* human-readable name - note this is probably not localised,
|
||||
* although this is not ruled out for the future.
|
||||
*
|
||||
* Deprecated - please use nameForVariant
|
||||
*/
|
||||
std::string name() const;
|
||||
SG_DEPRECATED(std::string name() const);
|
||||
|
||||
/**
|
||||
* Human readable name of a variant
|
||||
@@ -101,28 +113,31 @@ public:
|
||||
|
||||
/**
|
||||
* syntactic sugar to get the localised description
|
||||
*
|
||||
* Deprecated - please use getLocalisedProp to get the variant-specific
|
||||
* description.
|
||||
*/
|
||||
std::string description() const;
|
||||
|
||||
SG_DEPRECATED(std::string description() const);
|
||||
|
||||
/**
|
||||
* access the raw property data in the package
|
||||
*/
|
||||
SGPropertyNode* properties() const;
|
||||
|
||||
|
||||
/**
|
||||
* hex-encoded MD5 sum of the download files
|
||||
*/
|
||||
std::string md5() const;
|
||||
|
||||
|
||||
std::string getLocalisedProp(const std::string& aName, const unsigned int vIndex = 0) const;
|
||||
|
||||
unsigned int revision() const;
|
||||
|
||||
|
||||
size_t fileSizeBytes() const;
|
||||
|
||||
|
||||
CatalogRef catalog() const
|
||||
{ return m_catalog; }
|
||||
|
||||
|
||||
bool matches(const SGPropertyNode* aFilter) const;
|
||||
|
||||
string_set tags() const;
|
||||
@@ -131,13 +146,15 @@ public:
|
||||
* download URLs for the package
|
||||
*/
|
||||
string_list downloadUrls() const;
|
||||
|
||||
string_list thumbnailUrls() const;
|
||||
|
||||
/**
|
||||
* thumbnail file paths within the package on disk
|
||||
*/
|
||||
string_list thumbnails() const;
|
||||
|
||||
struct Thumbnail
|
||||
{
|
||||
std::string url;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
Thumbnail thumbnailForVariant(unsigned int vIndex) const;
|
||||
|
||||
/**
|
||||
* information about a preview image
|
||||
@@ -167,7 +184,7 @@ public:
|
||||
* retrieve all the thumbnails for a variant
|
||||
*/
|
||||
PreviewVec previewsForVariant(unsigned int vIndex) const;
|
||||
|
||||
|
||||
/**
|
||||
* Packages we depend upon.
|
||||
* If the dependency list cannot be satisifed for some reason,
|
||||
@@ -180,14 +197,23 @@ public:
|
||||
* same as the primary ID, depending on the aircraft author
|
||||
*/
|
||||
std::string dirName() const;
|
||||
|
||||
/**
|
||||
* Return the parent variant of a variant. This will be the emtpy string if
|
||||
* the variant is primary (top-level), otherwise the local (non-qualified)
|
||||
* ID. This allows establishing a heirarchy of variants within the package.
|
||||
* Note at present most code assumes a maxiumum two-level deep heirarchy
|
||||
* (parents and children)
|
||||
*/
|
||||
std::string parentIdForVariant(unsigned int variantIndex) const;
|
||||
private:
|
||||
SGPath pathOnDisk() const;
|
||||
|
||||
friend class Catalog;
|
||||
friend class Root;
|
||||
|
||||
|
||||
Package(const SGPropertyNode* aProps, CatalogRef aCatalog);
|
||||
|
||||
|
||||
void initWithProps(const SGPropertyNode* aProps);
|
||||
|
||||
void updateFromProps(const SGPropertyNode* aProps);
|
||||
@@ -203,7 +229,7 @@ private:
|
||||
string_set m_tags;
|
||||
CatalogRef m_catalog;
|
||||
string_list m_variants;
|
||||
|
||||
|
||||
mutable function_list<InstallCallback> _install_cb;
|
||||
};
|
||||
|
||||
@@ -215,4 +241,3 @@ private:
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_PACKAGE_PACKAGE_HXX
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -477,7 +479,7 @@ void Root::installProgress(InstallRef aInstall, unsigned int aBytes, unsigned in
|
||||
|
||||
void Root::startNext(InstallRef aCurrent)
|
||||
{
|
||||
if (d->updateDeque.front() != aCurrent) {
|
||||
if (d->updateDeque.empty() || (d->updateDeque.front() != aCurrent)) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "current install of package not head of the deque");
|
||||
} else {
|
||||
d->updateDeque.pop_front();
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
|
||||
<thumbnail>http://foo.bar.com/thumb-floats.png</thumbnail>
|
||||
<thumbnail-path>thumb-floats.png</thumbnail-path>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
@@ -96,6 +99,8 @@
|
||||
<name>C172 with skis</name>
|
||||
<description>A plane with skis</description>
|
||||
|
||||
<variant-of>c172p</variant-of>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-skis.png</path>
|
||||
@@ -109,10 +114,33 @@
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172r</id>
|
||||
<name>C172R</name>
|
||||
<description>Equally good version of the C172</description>
|
||||
<variant-of>_package_</variant-of>
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172r-floats</id>
|
||||
<name>C172R-floats</name>
|
||||
<description>Equally good version of the C172 with floats</description>
|
||||
<variant-of>c172r</variant-of>
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||
<url>http://localhost:2000/catalogTest1/c172p.zip</url>
|
||||
|
||||
<!-- legacy thumbnails also supported -->
|
||||
<thumbnail>http://foo.bar.com/thumb-exterior.png</thumbnail>
|
||||
<thumbnail-path>exterior.png</thumbnail-path>
|
||||
</package>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "PropertyBasedElement.hxx"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "PropertyBasedMgr.hxx"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "PropertyInterpolationMgr.hxx"
|
||||
#include "PropertyInterpolator.hxx"
|
||||
#include "props.hxx"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "PropertyInterpolator.hxx"
|
||||
#include "props.hxx"
|
||||
|
||||
|
||||
@@ -602,6 +602,45 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A string value managed through an object and access methods.
|
||||
*
|
||||
* This class stores the std::string returned by the getter, so that
|
||||
* the value returned by c_str() is still valid.
|
||||
*
|
||||
* A read-only value will not have a setter; a write-only value will
|
||||
* not have a getter.
|
||||
*/
|
||||
template <class C>
|
||||
class SGStringValueMethods : public SGRawValue<const char*>
|
||||
{
|
||||
public:
|
||||
typedef std::string (C::*getter_t)() const;
|
||||
typedef void (C::*setter_t)(const std::string&);
|
||||
SGStringValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0)
|
||||
: _obj(obj), _getter(getter), _setter(setter) {}
|
||||
virtual ~SGStringValueMethods () {}
|
||||
virtual const char* getValue () const {
|
||||
SGStringValueMethods* pthis = const_cast<SGStringValueMethods*>(this);
|
||||
if (_getter) { pthis->_value = (_obj.*_getter)();
|
||||
return pthis->_value.c_str(); }
|
||||
else { return SGRawValue<const char*>::DefaultValue(); }
|
||||
}
|
||||
virtual bool setValue (const char* value) {
|
||||
if (_setter) { (_obj.*_setter)(value ? value : ""); return true; }
|
||||
else return false;
|
||||
}
|
||||
virtual SGRaw* clone () const {
|
||||
return new SGStringValueMethods(_obj, _getter, _setter);
|
||||
}
|
||||
private:
|
||||
C &_obj;
|
||||
getter_t _getter;
|
||||
setter_t _setter;
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An indexed value managed through an object and access methods.
|
||||
*
|
||||
|
||||
@@ -15,3 +15,6 @@ foreach( mylibfolder
|
||||
|
||||
endforeach( mylibfolder )
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
add_subdirectory(dem)
|
||||
endif(ENABLE_GDAL)
|
||||
|
||||
24
simgear/scene/dem/CMakeLists.txt
Normal file
24
simgear/scene/dem/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
ReaderWriterPGT.hxx
|
||||
SGDem.hxx
|
||||
SGDemLevel.hxx
|
||||
SGDemRoot.hxx
|
||||
SGDemSession.hxx
|
||||
SGDemTile.hxx
|
||||
SGMesh.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
ReaderWriterPGT.cxx
|
||||
SGDem.cxx
|
||||
SGDemLevel.cxx
|
||||
SGDemRoot.cxx
|
||||
SGDemSession.cxx
|
||||
SGDemTile.cxx
|
||||
SGDemTile_gdal.cxx
|
||||
SGMesh.cxx
|
||||
)
|
||||
|
||||
simgear_scene_component(dem scene/dem "${SOURCES}" "${HEADERS}")
|
||||
418
simgear/scene/dem/ReaderWriterPGT.cxx
Normal file
418
simgear/scene/dem/ReaderWriterPGT.cxx
Normal file
@@ -0,0 +1,418 @@
|
||||
// ReaderWriterPGT.cxx -- Provide a paged database for flightgear scenery.
|
||||
//
|
||||
// Copyright (C) 2010 - 2013 Mathias Froehlich
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include "ReaderWriterPGT.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <osg/PagedLOD>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
|
||||
#include <simgear/scene/dem/SGDem.hxx>
|
||||
#include <simgear/scene/dem/SGMesh.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include <simgear/scene/tgdb/BucketBox.hxx>
|
||||
#include <simgear/scene/model/ModelRegistry.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
// Cull away tiles that we watch from downside
|
||||
struct ReaderWriterPGT::CullCallback : public osg::NodeCallback {
|
||||
virtual ~CullCallback()
|
||||
{ }
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
const osg::BoundingSphere& nodeBound = node->getBound();
|
||||
// If the bounding sphere of the node is empty, there is nothing to do
|
||||
if (!nodeBound.valid())
|
||||
return;
|
||||
|
||||
// Culling away tiles that we look at from the downside.
|
||||
// This is done by computing the maximum distance we can
|
||||
// see something from the current eyepoint. If the sphere
|
||||
// that is defined by this radius does no intersects the
|
||||
// nodes sphere, then this tile is culled away.
|
||||
// Computing this radius happens by two rectangular triangles:
|
||||
// Let r be the view point. rmin is the minimum radius we find
|
||||
// a ground surface we need to look above. rmax is the
|
||||
// maximum object radius we expect any object.
|
||||
//
|
||||
// d1 d2
|
||||
// x----x----x
|
||||
// r\ rmin /rmax
|
||||
// \ | /
|
||||
// \ | /
|
||||
// \|/
|
||||
//
|
||||
// The distance from the eyepoint to the point
|
||||
// where the line of sight is perpandicular to
|
||||
// the radius vector with minimal height is
|
||||
// d1 = sqrt(r^2 - rmin^2).
|
||||
// The distance from the point where the line of sight
|
||||
// is perpandicular to the radius vector with minimal height
|
||||
// to the highest possible object on earth with radius rmax is
|
||||
// d2 = sqrt(rmax^2 - rmin^2).
|
||||
// So the maximum distance we can see something on the earth
|
||||
// from a viewpoint r is
|
||||
// d = d1 + d2
|
||||
|
||||
// This is the equatorial earth radius minus 450m,
|
||||
// little lower than Dead Sea.
|
||||
float rmin = 6378137 - 450;
|
||||
float rmin2 = rmin*rmin;
|
||||
// This is the equatorial earth radius plus 9000m,
|
||||
// little higher than Mount Everest.
|
||||
float rmax = 6378137 + 9000;
|
||||
float rmax2 = rmax*rmax;
|
||||
|
||||
// Check if we are looking from below any ground
|
||||
osg::Vec3 viewPoint = nv->getViewPoint();
|
||||
// blow the viewpoint up to a spherical earth with equatorial radius:
|
||||
osg::Vec3 sphericViewPoint = viewPoint;
|
||||
sphericViewPoint[2] *= 1.0033641;
|
||||
float r2 = sphericViewPoint.length2();
|
||||
if (r2 <= rmin2)
|
||||
return;
|
||||
|
||||
// Due to this line of sight computation, the visible tiles
|
||||
// are limited to be within a sphere with radius d1 + d2.
|
||||
float d1 = sqrtf(r2 - rmin2);
|
||||
float d2 = sqrtf(rmax2 - rmin2);
|
||||
// Note that we again base the sphere around elliptic view point,
|
||||
// but use the radius from the spherical computation.
|
||||
if (!nodeBound.intersects(osg::BoundingSphere(viewPoint, d1 + d2)))
|
||||
return;
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReaderWriterPGT::LocalOptions {
|
||||
LocalOptions(const osgDB::Options* options) :
|
||||
_options(options)
|
||||
{
|
||||
osg::ref_ptr<SGReaderWriterOptions> sgOptions;
|
||||
sgOptions = SGReaderWriterOptions::copyOrCreate(options);
|
||||
|
||||
std::string pageLevelsString;
|
||||
std::string meshResolutionString;
|
||||
std::string meshTexturing;
|
||||
|
||||
if (_options) {
|
||||
pageLevelsString = _options->getPluginStringData("SimGear::SPT_PAGE_LEVELS");
|
||||
meshResolutionString = _options->getPluginStringData("SimGear::SPT_MESH_RESOLUTION");
|
||||
meshTexturing = _options->getPluginStringData("SimGear::SPT_LOD_TEXTURING");
|
||||
}
|
||||
|
||||
// Get the default if nothing given from outside
|
||||
// ignore option - pagelevels come from mesh
|
||||
_pageLevels.push_back(1);
|
||||
_pageLevels.push_back(2);
|
||||
|
||||
// dem level 2 - 2, 4, 12 degrees
|
||||
_pageLevels.push_back(3);
|
||||
_pageLevels.push_back(4);
|
||||
_pageLevels.push_back(5);
|
||||
|
||||
// dem level 1 - 1/8, 1/4, 1/2 and 1 degree
|
||||
_pageLevels.push_back(6);
|
||||
_pageLevels.push_back(7);
|
||||
_pageLevels.push_back(8);
|
||||
_pageLevels.push_back(9);
|
||||
|
||||
_dem = sgOptions->getDem();
|
||||
|
||||
// Get the default if nothing given from outside
|
||||
if (meshResolutionString.empty()) {
|
||||
_meshResolution = 1;
|
||||
} else {
|
||||
// If configured from outside
|
||||
std::stringstream ss(meshResolutionString);
|
||||
while (ss.good()) {
|
||||
ss >> _meshResolution;
|
||||
}
|
||||
}
|
||||
|
||||
if ( meshTexturing.empty() ) {
|
||||
_textureMethod = SGMesh::TEXTURE_BLUEMARBLE;
|
||||
} else if ( meshTexturing == "bluemarble" ) {
|
||||
_textureMethod = SGMesh::TEXTURE_BLUEMARBLE;
|
||||
} else if ( meshTexturing == "raster" ) {
|
||||
_textureMethod = SGMesh::TEXTURE_RASTER;
|
||||
} else if ( meshTexturing == "debug" ) {
|
||||
_textureMethod = SGMesh::TEXTURE_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
bool isPageLevel(unsigned level) const
|
||||
{
|
||||
return std::find(_pageLevels.begin(), _pageLevels.end(), level) != _pageLevels.end();
|
||||
}
|
||||
|
||||
std::string getLodPathForBucketBox(const BucketBox& bucketBox) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "LOD/";
|
||||
for (std::vector<unsigned>::const_iterator i = _pageLevels.begin(); i != _pageLevels.end(); ++i) {
|
||||
if (bucketBox.getStartLevel() <= *i)
|
||||
break;
|
||||
ss << bucketBox.getParentBox(*i) << "/";
|
||||
}
|
||||
ss << bucketBox;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
float getRangeMultiplier() const
|
||||
{
|
||||
float rangeMultiplier = 2;
|
||||
if (!_options)
|
||||
return rangeMultiplier;
|
||||
std::stringstream ss(_options->getPluginStringData("SimGear::SPT_RANGE_MULTIPLIER"));
|
||||
ss >> rangeMultiplier;
|
||||
return rangeMultiplier;
|
||||
}
|
||||
|
||||
const osgDB::Options* _options;
|
||||
std::vector<unsigned> _pageLevels;
|
||||
|
||||
SGDemPtr _dem;
|
||||
unsigned _meshResolution;
|
||||
SGMesh::TextureMethod _textureMethod;
|
||||
};
|
||||
|
||||
ReaderWriterPGT::ReaderWriterPGT()
|
||||
{
|
||||
supportsExtension("pgt", "SimGear realtime paged terrain meta database.");
|
||||
}
|
||||
|
||||
ReaderWriterPGT::~ReaderWriterPGT()
|
||||
{
|
||||
}
|
||||
|
||||
const char*
|
||||
ReaderWriterPGT::className() const
|
||||
{
|
||||
return "simgear::ReaderWriterPGT";
|
||||
}
|
||||
|
||||
osgDB::ReaderWriter::ReadResult
|
||||
ReaderWriterPGT::readObject(const std::string& fileName, const osgDB::Options* options) const
|
||||
{
|
||||
// We get called with different extensions. To make sure search continues,
|
||||
// we need to return FILE_NOT_HANDLED in this case.
|
||||
if (osgDB::getLowerCaseFileExtension(fileName) != "pgt")
|
||||
return ReadResult(ReadResult::FILE_NOT_HANDLED);
|
||||
if (fileName != "state.pgt")
|
||||
return ReadResult(ReadResult::FILE_NOT_FOUND);
|
||||
|
||||
osg::StateSet* stateSet = new osg::StateSet;
|
||||
stateSet->setAttributeAndModes(new osg::CullFace);
|
||||
|
||||
std::string imageFileName = options->getPluginStringData("SimGear::FG_WORLD_TEXTURE");
|
||||
if (imageFileName.empty()) {
|
||||
imageFileName = options->getPluginStringData("SimGear::FG_ROOT");
|
||||
imageFileName = osgDB::concatPaths(imageFileName, "Textures");
|
||||
imageFileName = osgDB::concatPaths(imageFileName, "Globe");
|
||||
imageFileName = osgDB::concatPaths(imageFileName, "world.topo.bathy.200407.3x4096x2048.png");
|
||||
}
|
||||
if (osg::Image* image = osgDB::readImageFile(imageFileName, options)) {
|
||||
osg::Texture2D* texture = new osg::Texture2D;
|
||||
texture->setImage(image);
|
||||
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
|
||||
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP);
|
||||
stateSet->setTextureAttributeAndModes(0, texture);
|
||||
}
|
||||
|
||||
return stateSet;
|
||||
}
|
||||
|
||||
osgDB::ReaderWriter::ReadResult
|
||||
ReaderWriterPGT::readNode(const std::string& fileName, const osgDB::Options* options) const
|
||||
{
|
||||
LocalOptions localOptions(options);
|
||||
SG_LOG(SG_IO, SG_WARN, "ReaderWriterPGT::readNode - reading:" << fileName );
|
||||
|
||||
// The file name without path and without the pgt extension
|
||||
std::string strippedFileName = osgDB::getStrippedName(fileName);
|
||||
if (strippedFileName == "earth")
|
||||
return ReadResult(createTree(BucketBox(-180, -90, 360, 180), localOptions, true));
|
||||
|
||||
std::stringstream ss(strippedFileName);
|
||||
BucketBox bucketBox;
|
||||
ss >> bucketBox;
|
||||
if (ss.fail()) {
|
||||
SG_LOG(SG_IO, SG_WARN, "error reading:" << strippedFileName );
|
||||
return ReadResult::FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
BucketBox bucketBoxList[2];
|
||||
unsigned bucketBoxListSize = bucketBox.periodicSplit(bucketBoxList);
|
||||
if (bucketBoxListSize == 0)
|
||||
return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
if (bucketBoxListSize == 1)
|
||||
return ReadResult(createTree(bucketBoxList[0], localOptions, true));
|
||||
|
||||
assert(bucketBoxListSize == 2);
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
group->addChild(createTree(bucketBoxList[0], localOptions, true));
|
||||
group->addChild(createTree(bucketBoxList[1], localOptions, true));
|
||||
return ReadResult(group);
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node>
|
||||
ReaderWriterPGT::createTree(const BucketBox& bucketBox, const LocalOptions& options, bool topLevel) const
|
||||
{
|
||||
if (bucketBox.getIsBucketSize()) {
|
||||
std::string fileName;
|
||||
fileName = bucketBox.getBucket().gen_index_str() + std::string(".stg");
|
||||
|
||||
return createTileMesh(bucketBox, options._options);
|
||||
} else if (!topLevel && options.isPageLevel(bucketBox.getStartLevel())) {
|
||||
return createPagedLOD(bucketBox, options);
|
||||
} else {
|
||||
BucketBox bucketBoxList[100];
|
||||
unsigned numTiles = bucketBox.getSubDivision(bucketBoxList, 100);
|
||||
if (numTiles == 0)
|
||||
return 0;
|
||||
|
||||
if (numTiles == 1)
|
||||
return createTree(bucketBoxList[0], options, false);
|
||||
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
for (unsigned i = 0; i < numTiles; ++i) {
|
||||
osg::ref_ptr<osg::Node> node = createTree(bucketBoxList[i], options, false);
|
||||
if (!node.valid())
|
||||
continue;
|
||||
group->addChild(node.get());
|
||||
}
|
||||
if (!group->getNumChildren())
|
||||
return 0;
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node>
|
||||
ReaderWriterPGT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const
|
||||
{
|
||||
osg::PagedLOD* pagedLOD = new osg::PagedLOD;
|
||||
|
||||
pagedLOD->setCenterMode(osg::PagedLOD::USER_DEFINED_CENTER);
|
||||
SGSpheref sphere = bucketBox.getBoundingSphere();
|
||||
pagedLOD->setCenter(toOsg(sphere.getCenter()));
|
||||
pagedLOD->setRadius(sphere.getRadius());
|
||||
|
||||
pagedLOD->setCullCallback(new CullCallback);
|
||||
|
||||
osg::ref_ptr<osgDB::Options> localOptions;
|
||||
localOptions = static_cast<osgDB::Options*>(options._options->clone(osg::CopyOp()));
|
||||
// FIXME:
|
||||
// The particle systems have nodes with culling disabled.
|
||||
// PagedLOD nodes with childnodes like this will never expire.
|
||||
// So, for now switch them off.
|
||||
localOptions->setPluginStringData("SimGear::PARTICLESYSTEM", "OFF");
|
||||
pagedLOD->setDatabaseOptions(localOptions.get());
|
||||
|
||||
// The break point for the low level of detail to the high level of detail
|
||||
float rangeMultiplier = options.getRangeMultiplier();
|
||||
float range = rangeMultiplier*sphere.getRadius();
|
||||
|
||||
// Look for a low level of detail tile
|
||||
std::string lodPath = options.getLodPathForBucketBox(bucketBox);
|
||||
const char* extensions[] = { ".btg.gz", ".flt" };
|
||||
for (unsigned i = 0; i < sizeof(extensions)/sizeof(extensions[0]); ++i) {
|
||||
std::string fileName = osgDB::findDataFile(lodPath + extensions[i], options._options);
|
||||
if (fileName.empty())
|
||||
continue;
|
||||
osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(fileName, options._options);
|
||||
if (!node.valid())
|
||||
continue;
|
||||
pagedLOD->addChild(node.get(), range, std::numeric_limits<float>::max());
|
||||
break;
|
||||
}
|
||||
// Add the static sea level textured shell if there is nothing found
|
||||
if (pagedLOD->getNumChildren() == 0) {
|
||||
osg::ref_ptr<osg::Node> node = createTileMesh(bucketBox, options._options);
|
||||
if (node.valid())
|
||||
pagedLOD->addChild(node.get(), range, std::numeric_limits<float>::max());
|
||||
}
|
||||
|
||||
// Add the paged file name that creates the subtrees on demand
|
||||
std::stringstream ss;
|
||||
ss << bucketBox << ".pgt";
|
||||
pagedLOD->setFileName(pagedLOD->getNumChildren(), ss.str());
|
||||
pagedLOD->setRange(pagedLOD->getNumChildren(), 0.0, range);
|
||||
|
||||
return pagedLOD;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node>
|
||||
ReaderWriterPGT::createTileMesh(const BucketBox& bucketBox, const LocalOptions& options) const
|
||||
{
|
||||
if (options._options->getPluginStringData("SimGear::FG_EARTH") != "ON")
|
||||
return 0;
|
||||
|
||||
SGSpheref sphere = bucketBox.getBoundingSphere();
|
||||
osg::Matrixd transform;
|
||||
transform.makeTranslate(toOsg(-sphere.getCenter()));
|
||||
|
||||
// TODO : return geode, not geometry - so we texture in SGMesh
|
||||
// osg::Geometry* geometry = bucketBox.getTileTriangleMesh( options._dem, options._meshResolution, options._textureMethod );
|
||||
osg::Geode* geode = bucketBox.getTileTriangleMesh( options._dem, options._meshResolution, options._textureMethod, options._options );
|
||||
if ( geode ) {
|
||||
transform.makeTranslate(toOsg(sphere.getCenter()));
|
||||
osg::MatrixTransform* matrixTransform = new osg::MatrixTransform(transform);
|
||||
matrixTransform->setDataVariance(osg::Object::STATIC);
|
||||
matrixTransform->addChild(geode);
|
||||
|
||||
return matrixTransform;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::StateSet>
|
||||
ReaderWriterPGT::getLowLODStateSet(const LocalOptions& options) const
|
||||
{
|
||||
osg::ref_ptr<osgDB::Options> localOptions;
|
||||
localOptions = static_cast<osgDB::Options*>(options._options->clone(osg::CopyOp()));
|
||||
localOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL);
|
||||
|
||||
osg::ref_ptr<osg::Object> object = osgDB::readRefObjectFile("state.pgt", localOptions.get());
|
||||
if (!dynamic_cast<osg::StateSet*>(object.get()))
|
||||
return 0;
|
||||
|
||||
return static_cast<osg::StateSet*>(object.get());
|
||||
}
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
// simgear::ModelRegistryCallbackProxy<simgear::LoadOnlyCallback> g_pgtCallbackProxy("pgt");
|
||||
53
simgear/scene/dem/ReaderWriterPGT.hxx
Normal file
53
simgear/scene/dem/ReaderWriterPGT.hxx
Normal file
@@ -0,0 +1,53 @@
|
||||
// ReaderWriterSPT.cxx -- Provide a paged database for flightgear scenery.
|
||||
//
|
||||
// Copyright (C) 2010 - 2013 Mathias Froehlich
|
||||
//
|
||||
// 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 _READERWRITERPGT_HXX
|
||||
#define _READERWRITERPGT_HXX
|
||||
|
||||
#include <osgDB/ReaderWriter>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
class BucketBox;
|
||||
|
||||
class ReaderWriterPGT : public osgDB::ReaderWriter {
|
||||
public:
|
||||
ReaderWriterPGT();
|
||||
virtual ~ReaderWriterPGT();
|
||||
|
||||
virtual const char* className() const;
|
||||
|
||||
virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& fileName, const osgDB::Options* options) const;
|
||||
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& fileName, const osgDB::Options* options) const;
|
||||
|
||||
protected:
|
||||
struct LocalOptions;
|
||||
|
||||
osg::ref_ptr<osg::Node> createTree(const BucketBox& bucketBox, const LocalOptions& options, bool topLevel) const;
|
||||
osg::ref_ptr<osg::Node> createPagedLOD(const BucketBox& bucketBox, const LocalOptions& options) const;
|
||||
osg::ref_ptr<osg::Node> createTileMesh(const BucketBox& bucketBox, const LocalOptions& options) const;
|
||||
osg::ref_ptr<osg::StateSet> getLowLODStateSet(const LocalOptions& options) const;
|
||||
|
||||
private:
|
||||
struct CullCallback;
|
||||
};
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
#endif
|
||||
332
simgear/scene/dem/SGDem.cxx
Normal file
332
simgear/scene/dem/SGDem.cxx
Normal file
@@ -0,0 +1,332 @@
|
||||
// SGDem.cxx -- read, write DEM heiarchy
|
||||
//
|
||||
// Written by Peter Sadrozinski, started August 2016.
|
||||
//
|
||||
// Copyright (C) 2001 - 2003 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <cpl_conv.h> // for CPLMalloc()
|
||||
#include "ogr_spatialref.h"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/stdint.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
#include <simgear/scene/dem/SGDem.hxx>
|
||||
#include <simgear/scene/dem/SGDemSession.hxx>
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
#define DEM_DEBUG (0)
|
||||
|
||||
// where to add these...
|
||||
int SGDem::floorWithEpsilon(double x)
|
||||
{
|
||||
return static_cast<int>(floor(x + SG_EPSILON));
|
||||
}
|
||||
|
||||
unsigned SGDem::normalizeLongitude(unsigned offset)
|
||||
{
|
||||
return offset - (360*8)*(offset/(360*8));
|
||||
}
|
||||
|
||||
unsigned SGDem::longitudeDegToOffset(double lon)
|
||||
{
|
||||
unsigned offset = (unsigned)( floorWithEpsilon( 8.0 * (lon + 180.0) ) );
|
||||
return normalizeLongitude(offset);
|
||||
}
|
||||
|
||||
double SGDem::offsetToLongitudeDeg(unsigned offset)
|
||||
{
|
||||
return offset*0.125 - 180;
|
||||
}
|
||||
|
||||
unsigned SGDem::latitudeDegToOffset(double lat)
|
||||
{
|
||||
if (lat < -90)
|
||||
return 0;
|
||||
|
||||
unsigned offset = (unsigned)( floorWithEpsilon( 8.0 * (lat + 90.0) ) );
|
||||
if (8*180 < offset)
|
||||
return 8*180;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
double SGDem::offsetToLatitudeDeg(unsigned offset)
|
||||
{
|
||||
return offset*0.125 - 90;
|
||||
}
|
||||
|
||||
int SGDem::addRoot( const SGPath& root )
|
||||
{
|
||||
GDALAllRegister();
|
||||
|
||||
SGDemRoot demRoot( root );
|
||||
|
||||
// collect subdir for each dem level - format is level_X
|
||||
if ( root.isDir() ) {
|
||||
Dir d(root);
|
||||
if (d.exists() ) {
|
||||
PathList levelPaths = d.children(Dir::TYPE_DIR);
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, levelPaths.size() << " Directories in " << d.path() );
|
||||
|
||||
for(const SGPath& p : levelPaths) {
|
||||
std::string prefix;
|
||||
int level;
|
||||
|
||||
std::istringstream iss( p.file() );
|
||||
getline(iss, prefix, '_');
|
||||
if ( ((iss.rdstate() & std::ifstream::failbit ) == 0 ) &&
|
||||
(prefix == "level" ) ) {
|
||||
iss >> level;
|
||||
|
||||
// read the deminfo.txt file
|
||||
SGPath infoFile = p / "deminfo.txt";
|
||||
SGPath extentsFile = p / "demextents.bin";
|
||||
|
||||
if ( infoFile.exists() ) {
|
||||
std::fstream demInfo(infoFile.c_str(), std::ios_base::in);
|
||||
int w, h, x, y, o;
|
||||
std::string ext;
|
||||
unsigned long extents[45][180];
|
||||
|
||||
demInfo >> w >> h >> x >> y >> o >> ext;
|
||||
|
||||
// search level for extents
|
||||
memset( (unsigned char*)extents, 0, sizeof(extents) );
|
||||
if ( extentsFile.exists() ) {
|
||||
std::fstream demExtents(extentsFile.c_str(), std::ios_base::in | std::ios_base::binary );
|
||||
|
||||
for ( unsigned int i=0; i<45; i++ ) {
|
||||
for ( unsigned int j=0; j<180; j++ ) {
|
||||
demExtents >> extents[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " found DEM level " << level << " in directory " << p << " width : " << w << " height : " << h << " xres : " << x << " yres : " << y << " overlap : " << o << " extension: " << ext );
|
||||
demRoot.addLevel( SGDemLevel( level, p, w, h, x, y, o, &extents[0][0], ext, true ) );
|
||||
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " found DEM level " << level << " in directory " << p << " without info file " );
|
||||
}
|
||||
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, " invalid dem level dir " << p << " got prefix " << prefix );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( demRoot.numLevels() ) {
|
||||
demRoots.push_back( demRoot );
|
||||
}
|
||||
|
||||
return demRoot.numLevels();
|
||||
}
|
||||
|
||||
int SGDem::createRoot( const SGPath& root )
|
||||
{
|
||||
// collect subdir for each dem level - format is level_X
|
||||
// create the directory
|
||||
SGPath newDir = root / "dummy";
|
||||
newDir.create_dir();
|
||||
|
||||
demRoots.push_back( SGDemRoot(root) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned SGDem::roundDown( unsigned offset, unsigned roundTo )
|
||||
{
|
||||
if ( roundTo == 0 ) {
|
||||
return offset;
|
||||
} else {
|
||||
return (offset / roundTo) * roundTo;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SGDem::roundUp( unsigned offset, unsigned roundTo )
|
||||
{
|
||||
if ( roundTo == 0 ) {
|
||||
return offset;
|
||||
} else {
|
||||
return ((offset+roundTo-1) / roundTo) * roundTo;
|
||||
}
|
||||
}
|
||||
|
||||
SGDemSession SGDem::openSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int level, bool cache )
|
||||
{
|
||||
SGDemSession s;
|
||||
|
||||
// Create the session
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - level " << level );
|
||||
|
||||
if ( level >= 0 ) {
|
||||
SGDemRoot* demRoot = findDem( wo, so, eo, no, level );
|
||||
|
||||
if ( demRoot ) {
|
||||
// traverse the demRoots, to see if any have this level
|
||||
unsigned w = demRoot->getWidth( level );
|
||||
unsigned h = demRoot->getHeight( level );
|
||||
unsigned x = demRoot->getResX( level );
|
||||
unsigned y = demRoot->getResY( level );
|
||||
unsigned o = demRoot->getOverlap( level );
|
||||
|
||||
s = SGDemSession(wo, so, eo, no, level, w, h, demRoot);
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - from offsets " << wo << ", " << so << " to offsets " << eo << ", " << no );
|
||||
|
||||
// calc min offsets based on tile widths and height for this level
|
||||
unsigned min_lon = roundDown( wo, w );
|
||||
unsigned min_lat = roundDown( so, h );
|
||||
unsigned max_lon = roundUp( eo, w );
|
||||
unsigned max_lat = roundUp( no, h );
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - from pre level rounding offsets " << min_lon << ", " << min_lat << " to offsets " << max_lon << ", " << max_lat );
|
||||
|
||||
for (unsigned lon = min_lon; lon < max_lon; lon += w) {
|
||||
for (unsigned lat = min_lat; lat < max_lat; lat += h) {
|
||||
s.addTile( demRoot->getOrCreateTile( lon, lat, w, h, x, y, o, level, cache ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - could not find DEM for " << wo << ", " << so << " - " << eo << ", " << no << " level " << level );
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_ALERT, "SGDem::OpenSession - invalid level " << level );
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
SGDemSession SGDem::openSession( const SGGeod& min, const SGGeod& max, const SGPath& input )
|
||||
{
|
||||
#define FP_ROUNDOFF_OUTSIDE (0.1)
|
||||
|
||||
// create a new demRoot for creation
|
||||
SGDemRoot demRoot(input);
|
||||
|
||||
// open all tiles between min and max
|
||||
int min_lon = (int)(floor(min.getLongitudeDeg()-FP_ROUNDOFF_OUTSIDE));
|
||||
int min_lat = (int)(floor(min.getLatitudeDeg()-FP_ROUNDOFF_OUTSIDE));
|
||||
int max_lon = (int)(ceil(max.getLongitudeDeg()+FP_ROUNDOFF_OUTSIDE));
|
||||
int max_lat = (int)(ceil(max.getLatitudeDeg()+FP_ROUNDOFF_OUTSIDE));
|
||||
|
||||
// Create the session
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - create sesion obj - req from " <<
|
||||
min.getLongitudeDeg() << ", " << min.getLatitudeDeg() << " to " <<
|
||||
max.getLongitudeDeg() << ", " << max.getLatitudeDeg() << " - getting " <<
|
||||
min_lon << ", " << min_lat << " to " << max_lon << ", " << max_lat );
|
||||
|
||||
SGDemSession s(min_lon, min_lat, max_lon, max_lat, &demRoot);
|
||||
|
||||
// todo - read a tile from the input dir
|
||||
int w = 1;
|
||||
int h = 1;
|
||||
int x = 1201;
|
||||
int y = 1201;
|
||||
int o = 32;
|
||||
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - Traverse tiles");
|
||||
for (int lon = min_lon; lon < max_lon; lon += w) {
|
||||
for (int lat = min_lat; lat < max_lat; lat += h) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::OpenSession - Create tile " <<
|
||||
lon << ", " << lat << " from dir " << input );
|
||||
|
||||
unsigned wo = (lon+180)*8;
|
||||
unsigned so = (lat+ 90)*8;
|
||||
|
||||
SGDemTile* pTile = new SGDemTile( input, wo, so, w, h, x, y, o, false );
|
||||
s.addTile( pTile );
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
SGDemRoot* SGDem::findDem( unsigned wo, unsigned so, unsigned eo, unsigned no, int lvl )
|
||||
{
|
||||
SGDemRoot* dr = NULL;
|
||||
|
||||
for ( unsigned int i=0; i<demRoots.size(); i++ )
|
||||
{
|
||||
if ( demRoots[i].isValid( lvl, wo, so, eo, no ) ) {
|
||||
dr = &demRoots[i];
|
||||
break;
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_ALERT, "SGDem::findDem - dem " << i << " is not vald." );
|
||||
}
|
||||
}
|
||||
|
||||
return dr;
|
||||
}
|
||||
|
||||
#if 0 // todo - move to session
|
||||
unsigned short SGDem::getAlt( const SGDemSession& s, const SGGeod& loc ) const
|
||||
{
|
||||
int alt = 0;
|
||||
|
||||
// which level index are we querying?
|
||||
int lvlIndex = s.getLvlIndex();
|
||||
|
||||
if ( lvlIndex >= 0 ) {
|
||||
// be careful with coordinates that lie on session boundaries -
|
||||
// on min, ok.
|
||||
// on max - make sure we select the tile in the session...
|
||||
int lvlWidth = levels[lvlIndex].getWidth();
|
||||
int lvlHeight = levels[lvlIndex].getHeight();
|
||||
|
||||
int intLon, intLat;
|
||||
|
||||
// we need to find the correct tile.
|
||||
// shift by 180, 90 to use 0 based ints
|
||||
intLon = (int)(round(loc.getLongitudeDeg())) + 180;
|
||||
intLon = (intLon / lvlWidth) * lvlWidth;
|
||||
intLon = intLon - 180;
|
||||
if ( intLon == s.getMaxLon() ) {
|
||||
intLon -= levels[lvlIndex].getWidth();
|
||||
}
|
||||
|
||||
intLat = (int)(round(loc.getLatitudeDeg())) + 90;
|
||||
intLat = (intLat / lvlHeight) * lvlHeight;
|
||||
intLat = intLat - 90;
|
||||
if ( intLat == s.getMaxLat() ) {
|
||||
intLat -= levels[lvlIndex].getHeight();
|
||||
}
|
||||
|
||||
unsigned long key = (intLon + 180) << 16 | (intLat + 90);
|
||||
|
||||
SGDemCache::const_iterator it = caches[lvlIndex].find(key);
|
||||
if ( it != caches[lvlIndex].end() ) {
|
||||
SGDemTileRef tile = it->second;
|
||||
alt = tile->getAlt( loc );
|
||||
} else {
|
||||
// fprintf( stderr, " Could NOT find tile %d,%d in cache %d key is %08lx\n", intLon, intLat, lvlIndex, key );
|
||||
alt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return alt;
|
||||
}
|
||||
#endif
|
||||
82
simgear/scene/dem/SGDem.hxx
Normal file
82
simgear/scene/dem/SGDem.hxx
Normal file
@@ -0,0 +1,82 @@
|
||||
// SGDem.hxx -- read, write DEM heiarchy
|
||||
//
|
||||
// Written by Peter Sadrozinski, started August 2016.
|
||||
//
|
||||
// Copyright (C) 2001 - 2003 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef __SG_DEM_HXX__
|
||||
#define __SG_DEM_HXX__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/scene/dem/SGDemRoot.hxx>
|
||||
|
||||
class SGDem : public SGReferenced
|
||||
{
|
||||
public:
|
||||
SGDem() {};
|
||||
~SGDem() {};
|
||||
|
||||
int addRoot( const SGPath& root );
|
||||
int createRoot( const SGPath& root );
|
||||
SGDemRoot* getRoot( unsigned int i ) {
|
||||
if ( i < demRoots.size() ) {
|
||||
return &demRoots[i];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getNumRoots( void ) {
|
||||
return demRoots.size();
|
||||
}
|
||||
|
||||
// todo : move to session
|
||||
// unsigned short getAlt( const SGDemSession& s, const SGGeod& loc ) const;
|
||||
|
||||
// find a Dem to satisfy a session - must have the level, and extents
|
||||
SGDemRoot* findDem( unsigned wo, unsigned so, unsigned eo, unsigned no, int lvl );
|
||||
|
||||
// open a session from a dem level - tiles will be read and reference counted until closed
|
||||
//SGDemSession openSession( const SGGeod& min, const SGGeod& max, int level, bool cache );
|
||||
SGDemSession openSession( unsigned wo, unsigned so, unsigned eo, unsigned no, int level, bool cache );
|
||||
|
||||
// open a session from an bare directory
|
||||
SGDemSession openSession( const SGGeod& min, const SGGeod& max, const SGPath& input );
|
||||
|
||||
// static helpers
|
||||
static int floorWithEpsilon( double x );
|
||||
static unsigned normalizeLongitude( unsigned offset );
|
||||
static unsigned longitudeDegToOffset( double lon );
|
||||
static double offsetToLongitudeDeg( unsigned offset );
|
||||
static unsigned latitudeDegToOffset( double lat );
|
||||
static double offsetToLatitudeDeg( unsigned offset );
|
||||
static unsigned roundDown( unsigned offset, unsigned roundTo );
|
||||
static unsigned roundUp( unsigned offset, unsigned roundTo );
|
||||
|
||||
private:
|
||||
std::vector<SGDemRoot> demRoots;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<SGDem> SGDemPtr;
|
||||
|
||||
#endif /* __SG_DEM_HXX__ */
|
||||
72
simgear/scene/dem/SGDemLevel.cxx
Normal file
72
simgear/scene/dem/SGDemLevel.cxx
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <simgear/scene/dem/SGDemLevel.hxx>
|
||||
|
||||
bool SGDemLevel::isValid( unsigned int wo, unsigned int so, unsigned int eo, unsigned int no ) const
|
||||
{
|
||||
bool valid = true;
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
void SGDemLevel::addExtent( unsigned int wo, unsigned int so, unsigned int eo, unsigned int no )
|
||||
{
|
||||
// add bitmap to level extent
|
||||
unsigned int minLon = (wo+0)/8;
|
||||
unsigned int maxLon = (eo+0)/8;
|
||||
|
||||
unsigned int minLat = (so+0)/8;
|
||||
unsigned int maxLat = (no+0)/8;
|
||||
|
||||
fprintf(stderr, "addExtent %u, %u - %u, %u - minLon %u, minLat %u, maxLon %u, maxLat %u\n", wo, so, eo, no, minLon, minLat, maxLon, maxLat );
|
||||
for ( unsigned int lat=minLat; lat<=maxLat; lat++ ) {
|
||||
// convert lon range to mask range
|
||||
unsigned char minMask = minLon/8;
|
||||
unsigned char maxMask = maxLon/8;
|
||||
|
||||
fprintf(stderr, "addExtent minMask %u, maxMask %d\n", minMask, maxMask );
|
||||
for ( unsigned int msk=minMask; msk<=maxMask; msk++ ) {
|
||||
if ( msk == minMask ) {
|
||||
// find beginning of mask
|
||||
unsigned char bitPos = minLon % 8;
|
||||
unsigned char bitMask = 0;
|
||||
for ( unsigned char i=0; i<=bitPos; i++ ) {
|
||||
bitMask = (bitMask >> 1) | 0x80;
|
||||
}
|
||||
fprintf(stderr, "addExtent minMask - lat %u, lonmsk %u, bits %u\n", lat, msk, bitMask );
|
||||
extent[lat][msk] |= bitMask;
|
||||
} else if ( msk == maxMask ) {
|
||||
// find end of mask
|
||||
unsigned char bitPos = maxLon % 8;
|
||||
unsigned char bitMask = 0;
|
||||
for ( unsigned int i=0; i<=bitPos; i++ ) {
|
||||
bitMask = (bitMask >> 1) | 0x80;
|
||||
}
|
||||
fprintf(stderr, "addExtent maxMask - lat %u, lonmsk %u, bits %x\n", lat, msk, bitMask );
|
||||
extent[lat][msk] |= bitMask;
|
||||
} else {
|
||||
fprintf(stderr, "addExtent middlemask - lat %u, lonmsk %u, bits %x\n", lat, msk, 0xFF );
|
||||
extent[lat][msk] = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SGDemLevel::close( void )
|
||||
{
|
||||
// save extent to file
|
||||
fprintf( stderr, "closing level\n" );
|
||||
SGPath extentFile = path / "demextent.txt";
|
||||
std::fstream demExtent(extentFile.c_str(), std::ios_base::out | std::ios_base::binary );
|
||||
|
||||
for ( unsigned char lat = 0; lat < 180; lat++ ) {
|
||||
for ( unsigned char lon = 0; lon < 360/8; lon++ ) {
|
||||
demExtent << extent[lat][lon];
|
||||
}
|
||||
}
|
||||
fprintf( stderr, "closing level complete\n" );
|
||||
}
|
||||
90
simgear/scene/dem/SGDemLevel.hxx
Normal file
90
simgear/scene/dem/SGDemLevel.hxx
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef __SG_DEM_LEVEL_HXX__
|
||||
#define __SG_DEM_LEVEL_HXX__
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
class SGDemLevel
|
||||
{
|
||||
public:
|
||||
SGDemLevel( int l, const SGPath& p, int w, int h, int x, int y, int o, const unsigned long* e, std::string ext, bool r )
|
||||
{
|
||||
level = l;
|
||||
path = p;
|
||||
width = w;
|
||||
height = h;
|
||||
xres = x;
|
||||
yres = y;
|
||||
overlap = o;
|
||||
extension = ext;
|
||||
ready = r;
|
||||
|
||||
std::fprintf( stderr, "copying extent fle size %lu\n", sizeof(extent) );
|
||||
std::memcpy( (void *)&extent[0][0], (const void *)e, sizeof(extent) );
|
||||
};
|
||||
|
||||
SGDemLevel( int l, const SGPath& p, int w, int h, int x, int y, int o, std::string ext, bool r )
|
||||
{
|
||||
level = l;
|
||||
path = p;
|
||||
width = w;
|
||||
height = h;
|
||||
xres = x;
|
||||
yres = y;
|
||||
overlap = o;
|
||||
extension = ext;
|
||||
ready = r;
|
||||
|
||||
std::fprintf( stderr, "setting extent fle size %lu\n", sizeof(extent) );
|
||||
memset( (unsigned char*)extent, 0, sizeof(extent) );
|
||||
};
|
||||
|
||||
int getLevel( void ) const {
|
||||
return level;
|
||||
}
|
||||
const SGPath& getLevelDir( void ) const {
|
||||
return path;
|
||||
}
|
||||
bool isReady( void ) const {
|
||||
return ready;
|
||||
}
|
||||
bool isValid( unsigned int wo, unsigned int so, unsigned int eo, unsigned int no ) const;
|
||||
|
||||
void addExtent( unsigned int wo, unsigned int so, unsigned int eo, unsigned int no );
|
||||
void close( void );
|
||||
|
||||
int getWidth( void ) const {
|
||||
return width;
|
||||
}
|
||||
int getHeight( void ) const {
|
||||
return height;
|
||||
}
|
||||
int getResX( void ) const {
|
||||
return xres;
|
||||
}
|
||||
int getResY( void ) const {
|
||||
return yres;
|
||||
}
|
||||
int getOverlap( void ) const {
|
||||
return overlap;
|
||||
}
|
||||
|
||||
private:
|
||||
SGPath path;
|
||||
std::string extension;
|
||||
bool ready;
|
||||
|
||||
int level;
|
||||
int width;
|
||||
int height;
|
||||
int xres;
|
||||
int yres;
|
||||
int overlap;
|
||||
|
||||
// bitmap of tiles (1x1 degree)
|
||||
unsigned char extent[180][360/8];
|
||||
};
|
||||
|
||||
#endif /* __SG_DEM_LEVEL_HXX__ */
|
||||
118
simgear/scene/dem/SGDemRoot.cxx
Normal file
118
simgear/scene/dem/SGDemRoot.cxx
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/scene/dem/SGDemRoot.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
bool SGDemRoot::isValid( int lvl, unsigned wo, unsigned so, unsigned eo, unsigned no ) const
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
// check if we have requested level
|
||||
if ( (unsigned int)lvl < levels.size() ) {
|
||||
// check if we fit in this levels extents
|
||||
valid = levels[lvl].isValid( wo, so, eo, no );
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDemRoot::isValid - lvl " << lvl << " is greater than #levels in root " << levels.size() );
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
int SGDemRoot::createLevel( int w, int h, int x, int y, int overlap, const std::string& ext )
|
||||
{
|
||||
int lvlidx = -1;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "level_" << std::setw(2) << std::setfill('0') << levels.size()+1;
|
||||
|
||||
// see if dir already exists
|
||||
SGPath lvlPath = demRoot / ss.str();
|
||||
if ( lvlPath.isDir() ) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::createLevel: found existing level directory at " << lvlPath.c_str() );
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::createLevel: creating level directory at " << lvlPath.c_str() );
|
||||
|
||||
// create the dir - needs the filename to do this !?!?
|
||||
SGPath infoFile = lvlPath / "deminfo.txt";
|
||||
int res = infoFile.create_dir();
|
||||
if ( res == 0 ) {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::createLevel: successfully created directory at " << infoFile.realpath().c_str() << " error " << res );
|
||||
|
||||
lvlidx = levels.size();
|
||||
|
||||
std::fstream demInfo(infoFile.c_str(), std::ios_base::out);
|
||||
demInfo << w << " " << h << " " << x << " " << y << " " << overlap << " " << ext << std::endl;
|
||||
|
||||
levels.push_back( SGDemLevel( lvlidx, lvlPath, w, h, x, y, overlap, ext, false ) );
|
||||
caches.push_back( SGDemCache() );
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_INFO, "SGDem::createLevel: can't create level directory at " << lvlPath.c_str() << " error " << res );
|
||||
}
|
||||
}
|
||||
|
||||
return lvlidx;
|
||||
}
|
||||
|
||||
void SGDemRoot::closeLevel( int lvl )
|
||||
{
|
||||
if ( (unsigned int)lvl < levels.size() ) {
|
||||
levels[lvl].close();
|
||||
}
|
||||
}
|
||||
|
||||
SGDemTileRef SGDemRoot::createTile( int lvl, int lon, int lat, int overlap, SGDemSession& s )
|
||||
{
|
||||
SGDemTileRef rTile = NULL;
|
||||
bool bWritten = false;
|
||||
|
||||
if ( lvl >= 0 ) {
|
||||
int w = levels[lvl].getWidth();
|
||||
int h = levels[lvl].getHeight();
|
||||
int x = levels[lvl].getResX();
|
||||
int y = levels[lvl].getResY();
|
||||
|
||||
unsigned wo = (unsigned)(lon+180)*8;
|
||||
unsigned so = (unsigned)(lat+ 90)*8;
|
||||
|
||||
rTile = new SGDemTile( levels[lvl].getLevelDir(),
|
||||
wo, so,
|
||||
w, h, x, y, overlap, s, bWritten );
|
||||
|
||||
if ( bWritten ) {
|
||||
fprintf( stderr, "CreateTile - add extent (lat%d+h%d+90)*8-1 is %u\n", lat, h, (lat+h+90)*8-1 );
|
||||
levels[lvl].addExtent( (lon+180)*8, (lat+90)*8, (lon+w+180)*8-1, (lat+h+90)*8-1 );
|
||||
}
|
||||
}
|
||||
|
||||
return rTile;
|
||||
}
|
||||
|
||||
void SGDemRoot::flushCaches( int lvl )
|
||||
{
|
||||
// traverse the map - delete any tiles with ref count == 1 ( us )
|
||||
//fprintf( stderr, "flush caches in lvl %d - num tiles is %lu\n", lvl, caches[lvl].size() );
|
||||
SGDemCache::iterator it = caches[lvl].begin();
|
||||
while ( it != caches[lvl].end() ) {
|
||||
if ( it->second.getNumRefs() == 1 ) {
|
||||
caches[lvl].erase(it++);
|
||||
} else {
|
||||
//fprintf( stderr, "can't flush tile - numrefs is %u\n", it->second.getNumRefs() );
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SGDemTileRef SGDemRoot::getTile( int lvlIndex, unsigned long key )
|
||||
{
|
||||
SGDemTileRef tile = NULL;
|
||||
|
||||
SGDemCache::const_iterator it = caches[lvlIndex].find(key);
|
||||
if ( it != caches[lvlIndex].end() ) {
|
||||
tile = it->second;
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
102
simgear/scene/dem/SGDemRoot.hxx
Normal file
102
simgear/scene/dem/SGDemRoot.hxx
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef __SG_DEM_ROOT_HXX__
|
||||
#define __SG_DEM_ROOT_HXX__
|
||||
|
||||
#include <simgear/scene/dem/SGDemLevel.hxx>
|
||||
#include <simgear/scene/dem/SGDemTile.hxx>
|
||||
|
||||
class SGDemRoot
|
||||
{
|
||||
public:
|
||||
SGDemRoot( SGPath p ) {
|
||||
demRoot = p;
|
||||
}
|
||||
|
||||
void addLevel( const SGDemLevel& level ) {
|
||||
levels.push_back( level );
|
||||
caches.push_back( SGDemCache() );
|
||||
}
|
||||
|
||||
// create a new level
|
||||
int createLevel( int w, int h, int x, int y, int overlap, const std::string& ext );
|
||||
SGDemTileRef createTile( int lvl, int lon, int lat, int overlap, SGDemSession& s );
|
||||
void closeLevel( int lvl );
|
||||
|
||||
unsigned int numLevels( void ) const {
|
||||
return levels.size();
|
||||
}
|
||||
|
||||
SGDemTileRef getTile( int lvlIndex, unsigned long key );
|
||||
|
||||
void flushCaches( int lvl );
|
||||
|
||||
bool isValid( int lvl, unsigned wo, unsigned so, unsigned eo, unsigned no ) const;
|
||||
|
||||
unsigned int getWidth( unsigned int level ) {
|
||||
if ( level < levels.size() ) {
|
||||
return levels[level].getWidth() * 8;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getHeight( unsigned int level ) {
|
||||
if ( level < levels.size() ) {
|
||||
return levels[level].getHeight() * 8;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getResX( unsigned int level ) {
|
||||
if ( level < levels.size() ) {
|
||||
return levels[level].getResX();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getResY( unsigned int level ) {
|
||||
if ( level < levels.size() ) {
|
||||
return levels[level].getResY();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getOverlap( unsigned int level ) {
|
||||
if ( level < levels.size() ) {
|
||||
return levels[level].getOverlap();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
SGDemTile* getOrCreateTile( unsigned wo, unsigned so,
|
||||
unsigned w, unsigned h, unsigned x, unsigned y,
|
||||
unsigned o, int level, bool cache )
|
||||
{
|
||||
unsigned long key = wo << 16 | so;
|
||||
|
||||
// is this tile already loaded?
|
||||
SGDemCache::const_iterator it = caches[level].find(key);
|
||||
if ( it != caches[level].end() ) {
|
||||
// yes - add the reference to this session
|
||||
//printf( "********************** adding ref to tile at %d,%d with key %lx\n", lon, lat, key );
|
||||
return it->second;
|
||||
} else {
|
||||
// no load the tile, now
|
||||
SGDemTile* pTile = new SGDemTile( levels[level].getLevelDir(),
|
||||
wo, so, w, h,
|
||||
x, y, o, cache );
|
||||
caches[level][key] = pTile;
|
||||
return pTile;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SGPath demRoot;
|
||||
std::vector<SGDemLevel> levels;
|
||||
std::vector<SGDemCache> caches;
|
||||
};
|
||||
|
||||
#endif /* __SG_DEM_ROOT_HXX__ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user