Compare commits

..

2 Commits

Author SHA1 Message Date
Automatic Release Builder
e084bd89e7 new version: 2017.1.2 2017-03-01 20:40:03 +01:00
James Turner
cfe9ddc2de Fix issues with package upgrades on Windows. 2017-02-23 13:27:02 +00:00
156 changed files with 978 additions and 8605 deletions

View File

@@ -120,14 +120,12 @@ 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)
@@ -271,13 +269,6 @@ 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)
@@ -344,8 +335,8 @@ if (NOT ${HAVE_STD_ISNAN})
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
set(WARNING_FLAGS_CXX "-Wall -fPIC")
set(WARNING_FLAGS_C "-Wall -fPIC")
set(WARNING_FLAGS_CXX "-Wall")
set(WARNING_FLAGS_C "-Wall")
if (CMAKE_VERSION VERSION_LESS 3.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
@@ -372,8 +363,8 @@ endif(CMAKE_COMPILER_IS_GNUCXX)
if (CLANG)
# Boost redeclares class members
set(WARNING_FLAGS_CXX "-Wall -fPIC -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall -fPIC")
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
# fix Boost compilation :(
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
@@ -390,19 +381,6 @@ 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)
@@ -486,8 +464,7 @@ set(TEST_LIBS_INTERNAL_CORE
${RT_LIBRARY}
${DL_LIBRARY}
${COCOA_LIBRARY}
${CURL_LIBRARIES}
${GDAL_LIBRARY})
${CURL_LIBRARIES})
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
if(NOT SIMGEAR_HEADLESS)

View File

@@ -17,9 +17,4 @@ 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")

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHMaterial.hxx"
namespace simgear {

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPageNode.hxx"
#include "BVHPager.hxx"

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPageRequest.hxx"
namespace simgear {

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHPager.hxx"
#include <list>

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "BVHStaticNode.hxx"
namespace simgear {

View File

@@ -15,7 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <iostream>
#include <simgear/structure/SGSharedPtr.hxx>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Canvas.hxx"
#include "CanvasEventManager.hxx"
#include "CanvasEventVisitor.hxx"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEvent.hxx"
namespace simgear

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEventManager.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>
#include <simgear/canvas/elements/CanvasElement.hxx>

View File

@@ -17,7 +17,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasEvent.hxx"
#include "CanvasEventVisitor.hxx"
#include <simgear/canvas/elements/CanvasElement.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMgr.hxx"
#include "Canvas.hxx"
#include "CanvasEventManager.hxx"

View File

@@ -19,8 +19,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Canvas.hxx"
#include "CanvasObjectPlacement.hxx"
#include <simgear/canvas/events/MouseEvent.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasPlacement.hxx"
#include <simgear/props/props.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMgr.hxx"
#include "CanvasSystemAdapter.hxx"
#include "CanvasWindow.hxx"

View File

@@ -35,7 +35,7 @@ void SHVector2_dtor(SHVector2 *v) {
}
void SHVector3_ctor(SHVector3 *v) {
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
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 SHIVA_USE_SIMD
#ifdef __SSE__
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 SHIVA_USE_SIMD
#ifdef __SSE__
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 SHIVA_USE_SIMD
#ifdef __SSE__
# ifdef __SSE3__
# include <pmmintrin.h>
inline float hsum_ps_sse(__m128 v) {

View File

@@ -21,17 +21,7 @@
#ifndef __SHVECTORS_H
#define __SHVECTORS_H
#ifdef HAVE_CONFIG_H
# include <simgear/simgear_config.h>
#endif
#ifdef ENABLE_SIMD
# ifdef __SSE__
// # define SHIVA_USE_SIMD
# endif
# endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# include <xmmintrin.h>
float hsum_ps_sse(__m128 v);
#endif
@@ -51,7 +41,7 @@ void SHVector2_dtor(SHVector2 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
@@ -66,7 +56,7 @@ void SHVector3_dtor(SHVector3 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
@@ -81,7 +71,7 @@ void SHVector4_dtor(SHVector4 *v);
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,w,h; };
@@ -98,7 +88,7 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
typedef struct
{
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
union ALIGN16 {
__m128 mtx[4];
SHfloat m[4][4];
@@ -127,7 +117,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
*--------------------------------------------------------- */
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
#else
@@ -136,7 +126,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SET3V(v1,v2) { v1.vec=v2.vec; }
# define SET4V(v1,v2) { v1.vec=v2.vec; }
#else
@@ -157,7 +147,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 SHIVA_USE_SIMD
#ifdef __SSE__
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
@@ -166,7 +156,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
#else
@@ -175,7 +165,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
@@ -184,7 +174,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
#else
@@ -193,7 +183,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define MUL2(v,f) { v.x*=f; v.y*=f; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
#else
@@ -202,7 +192,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define DIV2(v,f) { v.x/=f; v.y/=f; }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
#else
@@ -211,7 +201,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
#endif
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define ABS_MASK _mm_set1_ps(-0.f)
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
@@ -233,7 +223,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 SHIVA_USE_SIMD
#ifdef __SSE__
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
#else
@@ -247,7 +237,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 SHIVA_USE_SIMD
#ifdef __SSE__
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
#else
@@ -259,7 +249,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
* Macros for matrix operations
*-----------------------------------------------------*/
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
@@ -272,7 +262,7 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define SETMATMAT(m1, m2) { \
m1.mtx[0] = m2.mtx[0]; \
m1.mtx[1] = m2.mtx[1]; \
@@ -285,7 +275,7 @@ int i,j; \
m1.m[i][j] = m2.m[i][j]; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MULMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
@@ -298,7 +288,7 @@ int i,j; \
mat.m[i][j] *= s; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define DIVMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
@@ -311,7 +301,7 @@ int i,j; \
mat.m[i][j] /= s; }
#endif
#ifdef SHIVA_USE_SIMD
#ifdef __SSE__
# define MULMATMAT(m2, m1, mout) { \
int i,j; \
for (i=0;i<4;i++) { \

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasElement.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasEventVisitor.hxx>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasGroup.hxx"
#include "CanvasImage.hxx"
#include "CanvasMap.hxx"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasImage.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -17,7 +17,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasMap.hxx"
#include "map/geo_node_pair.hxx"
#include "map/projection.hxx"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasPath.hxx"
#include <simgear/scene/util/parse_color.hxx>
#include <simgear/misc/strutils.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CanvasText.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
@@ -497,31 +496,18 @@ namespace canvas
{
case LEFT_TO_RIGHT:
{
#if OSG_VERSION_LESS_THAN(3,5,2)
osg::Vec2 delta(activefont->getKerning(previous_charcode,
charcode,
_kerningType));
#else
osg::Vec2 delta(activefont->getKerning(_fontSize,
previous_charcode,
charcode,
_kerningType));
#endif
osg::Vec2 delta( activefont->getKerning( previous_charcode,
charcode,
_kerningType ) );
cursor.x() += delta.x() * wr;
cursor.y() += delta.y() * hr;
break;
}
case RIGHT_TO_LEFT:
{
#if OSG_VERSION_LESS_THAN(3,5,2)
osg::Vec2 delta(activefont->getKerning(charcode,
previous_charcode,
_kerningType));
#else
osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,
previous_charcode,
_kerningType));
#endif
osg::Vec2 delta( activefont->getKerning( charcode,
previous_charcode,
_kerningType ) );
cursor.x() -= delta.x() * wr;
cursor.y() -= delta.y() * hr;
break;

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "CustomEvent.hxx"
namespace simgear

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "DeviceEvent.hxx"
#include <osgGA/GUIEventAdapter>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "KeyboardEvent.hxx"
#include "utf8.h"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "MouseEvent.hxx"
#include <osgGA/GUIEventAdapter>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "KeyboardEvent.hxx"
#include <osgViewer/Viewer>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "BoxLayout.hxx"
#include "SpacerItem.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "Layout.hxx"
#include <simgear/debug/logstream.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "LayoutItem.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "NasalWidget.hxx"
#include <simgear/canvas/Canvas.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "SpacerItem.hxx"
namespace simgear

View File

@@ -109,13 +109,10 @@
# define SG_UNIX
#endif
#ifdef __GNUC__
#define SG_DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define SG_DEPRECATED(func) __declspec(deprecated) func
#if defined( __GNUC__ )
# define DEPRECATED __attribute__ ((deprecated))
#else
#pragma message("WARNING: You need to implement SG_DEPRECATED for this compiler")
#define SG_DEPRECATED(func) func
# define DEPRECATED
#endif
#if defined(__clang__)

View File

@@ -18,8 +18,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <simgear/debug/BufferedLogCallback.hxx>
#include <boost/foreach.hpp>

View File

@@ -46,7 +46,39 @@
#include <io.h>
#endif
const char* debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
default: return "unknown";
}
}
//////////////////////////////////////////////////////////////////////////////
@@ -70,40 +102,6 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
m_class = c;
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
default: return "unknown";
}
}
} // of namespace simgear
//////////////////////////////////////////////////////////////////////////////
@@ -179,7 +177,7 @@ public:
#endif
class logstream::LogStreamPrivate : public SGThread
class LogStreamPrivate : public SGThread
{
private:
/**
@@ -484,105 +482,52 @@ public:
/////////////////////////////////////////////////////////////////////////////
static std::unique_ptr<logstream> global_logstream;
static logstream* global_logstream = NULL;
static LogStreamPrivate* global_privateLogstream = NULL;
static SGMutex global_logStreamLock;
logstream::logstream()
{
d.reset(new LogStreamPrivate);
d->startLog();
global_privateLogstream = new LogStreamPrivate;
global_privateLogstream->startLog();
}
logstream::~logstream()
{
popup_msgs.clear();
d->stop();
global_privateLogstream->stop();
delete global_privateLogstream;
}
void
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
d->setLogLevels(c, p);
global_privateLogstream->setLogLevels(c, p);
}
void logstream::setDeveloperMode(bool devMode)
{
d->m_developerMode = devMode;
global_privateLogstream->m_developerMode = devMode;
}
void
logstream::addCallback(simgear::LogCallback* cb)
{
d->addCallback(cb);
global_privateLogstream->addCallback(cb);
}
void
logstream::removeCallback(simgear::LogCallback* cb)
{
d->removeCallback(cb);
global_privateLogstream->removeCallback(cb);
}
void
logstream::log( sgDebugClass c, sgDebugPriority p,
const char* fileName, int line, const std::string& msg)
{
d->log(c, p, fileName, line, msg);
}
void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, int columns)
{
unsigned int i, j;
char temp[3000], temp1[3000];
*temp = 0;
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
{
if (strlen(temp) > 500) return;
/* print offset */
if (i % columns == 0)
{
sprintf(temp1, "0x%06x: ", i);
strcat(temp, temp1);
}
/* print hex data */
if (i < len)
{
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
strcat(temp, temp1);
}
else /* end of block, just aligning for ASCII dump */
{
strcat(temp, " ");
}
/* print ASCII dump */
if (i % columns == (columns - 1))
{
for (j = i - (columns - 1); j <= i; j++)
{
if (j >= len) /* end of block, not really printing */
{
strcat(temp, " ");
}
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
{
char t2[2];
t2[0] = 0xFF & ((char*)mem)[j];
t2[1] = 0;
strcat(temp, t2);
}
else /* other char */
{
strcat(temp, ".");
}
}
log(c, p, fileName, line, temp );
*temp = 0;
}
}
global_privateLogstream->log(c, p, fileName, line, msg);
}
void
@@ -612,31 +557,31 @@ logstream::has_popup()
bool
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
{
return d->would_log(c,p);
return global_privateLogstream->would_log(c,p);
}
sgDebugClass
logstream::get_log_classes() const
{
return d->m_logClass;
return global_privateLogstream->m_logClass;
}
sgDebugPriority
logstream::get_log_priority() const
{
return d->m_logPriority;
return global_privateLogstream->m_logPriority;
}
void
logstream::set_log_priority( sgDebugPriority p)
{
d->setLogLevels(d->m_logClass, p);
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
}
void
logstream::set_log_classes( sgDebugClass c)
{
d->setLogLevels(c, d->m_logPriority);
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
}
@@ -652,54 +597,54 @@ sglog()
SGGuard<SGMutex> g(global_logStreamLock);
if( !global_logstream )
global_logstream.reset(new logstream);
return *(global_logstream.get());
global_logstream = new logstream();
return *global_logstream;
}
void
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
{
d->addCallback(new FileLogCallback(aPath, c, p));
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
}
void logstream::setStartupLoggingEnabled(bool enabled)
{
d->setStartupLoggingEnabled(enabled);
global_privateLogstream->setStartupLoggingEnabled(enabled);
}
void logstream::requestConsole()
namespace simgear
{
void requestConsole()
{
#if defined (SG_WINDOWS)
const bool stderrAlreadyRedirected = d->m_stderr_isRedirectedAlready;
const bool stdoutAlreadyRedirected = d->m_stdout_isRedirectedAlready;
/*
* 2016-09-20(RJH) - Reworked console handling
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
* we don't need to handle that.
* The new handling is quite simple:
* 1. The constructor will ensure that these streams exists. It will attach to the
* parent command prompt if started from the command prompt, otherwise the
* stdout/stderr will be bound to the NUL device.
* 2. with --console a window will always appear regardless of where the process was
* started from. Any non redirected streams will be redirected
* 3. You cannot use --console and either redirected stream.
*
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
*/
/*
* 2016-09-20(RJH) - Reworked console handling
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
* we don't need to handle that.
* The new handling is quite simple:
* 1. The constructor will ensure that these streams exists. It will attach to the
* parent command prompt if started from the command prompt, otherwise the
* stdout/stderr will be bound to the NUL device.
* 2. with --console a window will always appear regardless of where the process was
* started from. Any non redirected streams will be redirected
* 3. You cannot use --console and either redirected stream.
*
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
*/
if (!stderrAlreadyRedirected && !stdoutAlreadyRedirected) {
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
FreeConsole();
if (AllocConsole()) {
if (!stdoutAlreadyRedirected)
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
freopen("conout$", "w", stdout);
if (!stderrAlreadyRedirected)
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
freopen("conout$", "w", stderr);
//http://stackoverflow.com/a/25927081
//Clear the error state for each of the C++ standard stream objects.
//Clear the error state for each of the C++ standard stream objects.
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
@@ -712,19 +657,11 @@ void logstream::requestConsole()
}
namespace simgear
{
void requestConsole()
{
sglog().requestConsole();
}
void shutdownLogging()
{
SGGuard<SGMutex> g(global_logStreamLock);
global_logstream.reset();
delete global_logstream;
global_logstream = 0;
}
} // of namespace simgear

View File

@@ -30,8 +30,7 @@
#include <sstream>
#include <vector>
#include <memory>
// forward decls
class SGPath;
@@ -50,8 +49,6 @@ protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
@@ -77,14 +74,6 @@ public:
~logstream();
static void initGlobalLogstream();
/**
* Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the
* moment - on other plaforms it's a no-op
*/
void requestConsole();
/**
* Set the global log class and priority level.
* @param c debug class
@@ -117,12 +106,6 @@ 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
@@ -169,10 +152,6 @@ private:
logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d;
};
logstream& sglog();
@@ -189,14 +168,12 @@ logstream& sglog();
do { if(sglog().would_log(C,P)) { \
std::ostringstream os; os << M; \
sglog().log(C, P, __FILE__, __LINE__, os.str()); \
if ((P) == SG_POPUP) sglog().popup(os.str()); \
if (P == SG_POPUP) sglog().popup(os.str()); \
} } while(0)
#ifdef FG_NDEBUG
# define SG_LOG(C,P,M) do { if((P) == SG_POPUP) SG_LOGX(C,P,M) } while(0)
# define SG_HEXDUMP(C,P,MEM,LEN)
# define SG_LOG(C,P,M) do { if(P == SG_POPUP) SG_LOGX(C,P,M) } while(0)
#else
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
#endif
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)

View File

@@ -10,14 +10,13 @@ if(ENABLE_TESTS)
add_executable(test_metar test_metar.cxx)
if (SIMGEAR_SHARED)
target_link_libraries(test_metar SimGearScene ${GDAL_LIBRARY})
target_link_libraries(test_metar SimGearScene)
else()
target_link_libraries(test_metar
SimGearScene SimGearCore
${CMAKE_THREAD_LIBS_INIT}
${ZLIB_LIBRARY}
${RT_LIBRARY}
${GDAL_LIBRARY})
${RT_LIBRARY})
endif()
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)

View File

@@ -22,7 +22,6 @@
* $Id$
**************************************************************************/
#include <simgear_config.h>
#include <simgear/debug/logstream.hxx>
#include <math.h>

View File

@@ -22,7 +22,7 @@
* $Id$
**************************************************************************/
#include <simgear_config.h>
#include <string.h>
#include <simgear/debug/logstream.hxx>

View File

@@ -22,7 +22,6 @@
* $Id$
**************************************************************************/
#include <simgear_config.h>
#include <cmath>
#include <simgear/debug/logstream.hxx>

View File

@@ -21,7 +21,6 @@
// 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>
@@ -191,11 +190,11 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
r->ttl = result->dnsnaptr_ttl;
for (int i = 0; i < result->dnsnaptr_nrr; i++) {
if( !r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service )
continue;
return;
//TODO: case ignore and result flags may have more than one flag
if( !r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags )
continue;
return;
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
r->entries.push_back(naptr);

View File

@@ -21,7 +21,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "HTTPClient.hxx"
#include "HTTPFileRequest.hxx"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "HTTPFileRequest.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "HTTPMemoryRequest.hxx"
namespace simgear

View File

@@ -16,10 +16,10 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <simgear_config.h>
#include "HTTPRepository.hxx"
#include <simgear_config.h>
#include <iostream>
#include <cassert>
#include <algorithm>

View File

@@ -15,7 +15,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "HTTPRequest.hxx"
#include <simgear/compiler.h>

View File

@@ -1,4 +1,3 @@
#include <simgear_config.h>
#include <cstdio>
#include <cstring>

View File

@@ -1,4 +1,3 @@
#include <simgear_config.h>
#include <cstdio>
#include <cstring>

View File

@@ -4,14 +4,12 @@ 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}")
@@ -22,8 +20,4 @@ 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)

View File

@@ -20,7 +20,6 @@
//
// $Id$
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <string>
#include <ctype.h> // isspace()

View File

@@ -1,5 +1,3 @@
#include <simgear_config.h>
#include <iostream>
#include <cstdlib> // for EXIT_SUCCESS

View File

@@ -6,33 +6,27 @@
//
// 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 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 distributed in the hope that it will be useful,
// 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
// Library General Public License for more details.
// 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 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>
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <string>
#include <ios> // std::streamsize
#include <istream>
#include <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>
@@ -47,11 +41,6 @@
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)
@@ -86,53 +75,38 @@ static string zlibErrorMessage(const z_stream& zstream, int errorCode)
return res;
}
// Requirement: 'val' must be non-negative.
//
// Return:
// - 'val' cast as BoundType if it's lower than the largest value BoundType
// can represent;
// - this largest value otherwise.
template<class BoundType, class T>
static BoundType clipCast(T val)
// 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()
{
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
uLong flags = ::zlibCompileFlags();
std::size_t res;
// 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();
switch (flags & 0x3) {
case 0x3:
SG_LOG(SG_IO, SG_WARN,
"Unknown size for zlib's uInt type (code 3). Will assume 64 bits, "
"but the actual value is probably higher.");
// No 'break' here, this is intentional.
case 0x2:
res = 0xFFFFFFFFFFFFFFFF; // 2^64 - 1
break;
case 0x1:
res = 0xFFFFFFFF; // 2^32 - 1
break;
case 0x0:
res = 0xFFFF; // 2^16 - 1
break;
default:
throw std::logic_error("It should be impossible to get here.");
}
return res;
}
// 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
static const std::size_t zlibMaxChunk = ::zlibMaxChunkSize();
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
{
@@ -151,13 +125,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...
@@ -190,24 +164,6 @@ 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) {
@@ -258,8 +214,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);
@@ -270,7 +226,7 @@ ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
[[ noreturn ]] void ZlibAbstractIStreambuf::handleZ_BUF_ERROR() const
{
switch (operationType()) {
case OperationType::DECOMPRESSION:
case OPERATION_TYPE_DECOMPRESSION:
{
string message = (_path.isNull()) ?
"Got Z_BUF_ERROR from zlib while decompressing a stream. The stream "
@@ -281,15 +237,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 OperationType::COMPRESSION:
case OPERATION_TYPE_COMPRESSION:
throw std::logic_error(
"Called ZlibAbstractIStreambuf::handleZ_BUF_ERROR() with "
"operationType() == ZlibAbstractIStreambuf::OperationType::COMPRESSION");
"operationType() == OPERATION_TYPE_DECOMPRESSION");
default:
throw std::logic_error(
"Unexpected operationType() in "
"ZlibAbstractIStreambuf::handleZ_BUF_ERROR(): " +
std::to_string(enumValue(operationType())));
std::to_string(operationType()));
}
}
@@ -309,7 +265,9 @@ char* ZlibAbstractIStreambuf::fillOutputBuffer()
remainingSpaceInOutBuf = fOB_remainingSpace(_zstream.next_out);
while (remainingSpaceInOutBuf > 0) {
_zstream.avail_out = clipCast<uInt>(remainingSpaceInOutBuf);
// This does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
_zstream.avail_out = static_cast<uInt>(
std::min(remainingSpaceInOutBuf, zlibMaxChunk));
if (_zstream.avail_in == 0 && !allInputRead) {
// Get data from _iStream, store it in _inBuf
@@ -369,7 +327,9 @@ bool ZlibAbstractIStreambuf::getInputData()
// Data already available?
if (alreadyAvailable > 0) {
_zstream.avail_in = clipCast<uInt>(alreadyAvailable);
_zstream.avail_in = static_cast<uInt>(
std::min(static_cast<std::size_t>(alreadyAvailable),
zlibMaxChunk));
return allInputRead;
}
@@ -380,8 +340,9 @@ bool ZlibAbstractIStreambuf::getInputData()
// Fill the input buffer (as much as possible)
while (_inBufEndPtr < _inBuf + _inBufSize && !_iStream.eof()) {
std::streamsize nbCharsToRead = clipCast<std::streamsize>(
_inBuf + _inBufSize - _inBufEndPtr);
std::streamsize nbCharsToRead = std::min(
_inBuf + _inBufSize - _inBufEndPtr,
std::numeric_limits<std::streamsize>::max()); // max we can pass to read()
_iStream.read(_inBufEndPtr, nbCharsToRead);
if (_iStream.bad()) {
@@ -400,8 +361,10 @@ bool ZlibAbstractIStreambuf::getInputData()
std::ptrdiff_t availableChars =
_inBufEndPtr - reinterpret_cast<char*>(_zstream.next_in);
// assert(availableChars >= 0); <-- already done in clipCast<uInt>()
_zstream.avail_in = clipCast<uInt>(availableChars);
assert(availableChars >= 0);
_zstream.avail_in = static_cast<uInt>(
std::min(static_cast<std::size_t>(availableChars),
zlibMaxChunk));
if (_iStream.eof()) {
allInputRead = true;
@@ -421,34 +384,24 @@ bool ZlibAbstractIStreambuf::getInputData()
// (_outBuf) before being copied to its (hopefully) final destination.
std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
{
// Despite the somewhat misleading footnote 296 of §27.5.3 of the C++11
// standard, one can't assume std::size_t to be at least as large as
// std::streamsize (64 bits std::streamsize in a 32 bits Windows program).
std::streamsize remaining = n;
char* origGptr = gptr();
std::size_t remaining = static_cast<std::size_t>(n);
std::size_t chunkSize;
std::ptrdiff_t avail;
const char* origGptr = gptr();
char* writePtr = dest; // we'll need dest later -> work with a copy
// First, let's take data present in our internal buffer (_outBuf)
while (remaining > 0) {
// Number of available chars in _outBuf
std::ptrdiff_t avail = egptr() - gptr();
avail = egptr() - gptr(); // number of available chars in _outBuf
if (avail == 0) { // our internal buffer is empty
break;
}
// We need an int for gbump(), at least in C++11.
int chunkSize_i = clipCast<int>(avail);
if (chunkSize_i > remaining) {
chunkSize_i = static_cast<int>(remaining);
}
assert(chunkSize_i >= 0);
std::copy(gptr(), gptr() + chunkSize_i, writePtr);
gbump(chunkSize_i);
writePtr += chunkSize_i;
// This cast is okay because 0 <= chunkSize_i <= remaining, which is an
// std::streamsize
remaining -= static_cast<std::streamsize>(chunkSize_i);
chunkSize = std::min(remaining, static_cast<std::size_t>(avail));
std::copy(gptr(), gptr() + chunkSize, writePtr);
gbump(chunkSize);
writePtr += chunkSize;
remaining -= chunkSize;
}
if (remaining == 0) {
@@ -465,10 +418,9 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
int retCode;
while (remaining > 0) {
std::streamsize chunkSize_s = zlibChunk(remaining);
// chunkSize_s > 0 and does fit in a zlib uInt: that's the whole point of
// zlibChunk.
_zstream.avail_out = static_cast<uInt>(chunkSize_s);
chunkSize = std::min(remaining, zlibMaxChunk);
// It does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
_zstream.avail_out = static_cast<uInt>(chunkSize);
if (_zstream.avail_in == 0 && !allInputRead) {
allInputRead = getInputData();
@@ -477,9 +429,8 @@ 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_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);
// chunkSize - _zstream.avail_out is the nb of chars written by zlib
remaining -= chunkSize - _zstream.avail_out;
if (retCode == Z_BUF_ERROR) {
handleZ_BUF_ERROR(); // doesn't return
@@ -493,23 +444,12 @@ std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
}
}
// 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
// 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);
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,
@@ -532,33 +472,38 @@ std::size_t ZlibAbstractIStreambuf::xsgetn_preparePutbackArea(
//
// [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) {
std::size_t chunkSize = nbPutbackChars - inDestBuffer; // yes, this number
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
return nbPutbackChars;
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;
}
// ***************************************************************************
@@ -582,24 +527,6 @@ 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
@@ -613,7 +540,7 @@ ZlibCompressorIStreambuf::~ZlibCompressorIStreambuf()
ZlibAbstractIStreambuf::OperationType
ZlibCompressorIStreambuf::operationType() const
{
return OperationType::COMPRESSION;
return OPERATION_TYPE_COMPRESSION;
}
void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
@@ -622,30 +549,30 @@ void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
{
int windowBits, memLevel;
// Intentionally not listing ZLibCompressionFormat::AUTODETECT here (it is
// Intentionally not listing ZLIB_COMPRESSION_FORMAT_AUTODETECT here (it is
// only for decompression!)
switch (format) {
case ZLibCompressionFormat::ZLIB:
case ZLIB_COMPRESSION_FORMAT_ZLIB:
windowBits = 15;
break;
case ZLibCompressionFormat::GZIP:
case ZLIB_COMPRESSION_FORMAT_GZIP:
windowBits = 31;
break;
default:
throw std::logic_error("Unexpected compression format: " +
std::to_string(enumValue(format)));
std::to_string(format));
}
switch (memStrategy) {
case ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED:
case ZLIB_FAVOR_MEMORY_OVER_SPEED:
memLevel = 8;
break;
case ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY:
case ZLIB_FAVOR_SPEED_OVER_MEMORY:
memLevel = 9;
break;
default:
throw std::logic_error("Unexpected memory strategy: " +
std::to_string(enumValue(memStrategy)));
std::to_string(memStrategy));
}
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
@@ -688,21 +615,6 @@ 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
@@ -716,7 +628,7 @@ ZlibDecompressorIStreambuf::~ZlibDecompressorIStreambuf()
ZlibAbstractIStreambuf::OperationType
ZlibDecompressorIStreambuf::operationType() const
{
return OperationType::DECOMPRESSION;
return OPERATION_TYPE_DECOMPRESSION;
}
void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
@@ -724,18 +636,18 @@ void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
int windowBits;
switch (format) {
case ZLibCompressionFormat::ZLIB:
case ZLIB_COMPRESSION_FORMAT_ZLIB:
windowBits = 15;
break;
case ZLibCompressionFormat::GZIP:
case ZLIB_COMPRESSION_FORMAT_GZIP:
windowBits = 31;
break;
case ZLibCompressionFormat::AUTODETECT:
case ZLIB_COMPRESSION_FORMAT_AUTODETECT:
windowBits = 47; // 47 = 32 + 15
break;
default:
throw std::logic_error("Unexpected compression format: " +
std::to_string(enumValue(format)));
std::to_string(format));
}
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
@@ -772,31 +684,10 @@ ZlibCompressorIStream::ZlibCompressorIStream(std::istream& iStream,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: std::istream(nullptr),
_streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
: _streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
inBufSize, outBuf, outBufSize, putbackSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
}
ZlibCompressorIStream::ZlibCompressorIStream(
std::unique_ptr<std::istream> iStream_p,
const SGPath& path,
int compressionLevel,
ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: std::istream(nullptr),
_streamBuf(std::move(iStream_p), path, compressionLevel, format,
memStrategy, inBuf, inBufSize, outBuf, outBufSize, putbackSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
}
ZlibCompressorIStream::~ZlibCompressorIStream()
@@ -814,29 +705,10 @@ ZlibDecompressorIStream::ZlibDecompressorIStream(std::istream& iStream,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: std::istream(nullptr),
_streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
: _streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
putbackSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
}
ZlibDecompressorIStream::ZlibDecompressorIStream(
std::unique_ptr<std::istream> iStream_p,
const SGPath& path,
ZLibCompressionFormat format,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: std::istream(nullptr),
_streamBuf(std::move(iStream_p), path, format, inBuf, inBufSize,
outBuf, outBufSize, putbackSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
}
ZlibDecompressorIStream::~ZlibDecompressorIStream()

View File

@@ -6,28 +6,25 @@
//
// 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 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 distributed in the hope that it will be useful,
// 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
// Library General Public License for more details.
// 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 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.
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#ifndef SG_ZLIBSTREAM_HXX
#define SG_ZLIBSTREAM_HXX
#include <iosfwd>
#include <ios> // std::streamsize
#include <istream>
#include <streambuf>
#include <memory> // std::unique_ptr
#include <zlib.h> // struct z_stream
#include <simgear/misc/sg_path.hxx>
@@ -100,15 +97,15 @@
namespace simgear
{
enum class ZLibCompressionFormat {
ZLIB = 0,
GZIP,
AUTODETECT
enum ZLibCompressionFormat {
ZLIB_COMPRESSION_FORMAT_ZLIB = 0,
ZLIB_COMPRESSION_FORMAT_GZIP,
ZLIB_COMPRESSION_FORMAT_AUTODETECT
};
enum class ZLibMemoryStrategy {
FAVOR_MEMORY_OVER_SPEED = 0,
FAVOR_SPEED_OVER_MEMORY
enum ZLibMemoryStrategy {
ZLIB_FAVOR_MEMORY_OVER_SPEED = 0,
ZLIB_FAVOR_SPEED_OVER_MEMORY
};
// Abstract base class for both the compressor and decompressor stream buffers.
@@ -147,27 +144,14 @@ 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;
virtual ~ZlibAbstractIStreambuf();
~ZlibAbstractIStreambuf();
protected:
enum class OperationType {
COMPRESSION = 0,
DECOMPRESSION
enum OperationType {
OPERATION_TYPE_COMPRESSION = 0,
OPERATION_TYPE_DECOMPRESSION
};
virtual OperationType operationType() const = 0;
@@ -182,11 +166,6 @@ 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
@@ -195,15 +174,12 @@ protected:
private:
// Callback whose role is to refill the output buffer when it's empty and
// the “client” tries to read more.
virtual int underflow() override;
int underflow() override;
// Optional override when subclassing std::streambuf. This is the most
// efficient way of reading several characters (as soon as we've emptied the
// output buffer, data is written by zlib directly to the destination
// buffer).
virtual std::streamsize xsgetn(char* dest, std::streamsize n) override;
// Utility method for xsgetn()
std::size_t xsgetn_preparePutbackArea(char* origGptr, char* dest,
char* writePtr);
std::streamsize xsgetn(char* dest, std::streamsize n) override;
// Make sure there is data to read in the input buffer, or signal EOF.
bool getInputData();
// Utility method for fillOutputBuffer()
@@ -291,46 +267,32 @@ public:
// the highest compression speed but worst compression
// ratio, and 9 the highest compression ratio but lowest
// compression speed.
// format either ZLibCompressionFormat::ZLIB or
// ZLibCompressionFormat::GZIP
// memStrategy either ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED or
// ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY
// format either ZLIB_COMPRESSION_FORMAT_ZLIB or
// ZLIB_COMPRESSION_FORMAT_GZIP
// memStrategy either ZLIB_FAVOR_MEMORY_OVER_SPEED or
// ZLIB_FAVOR_SPEED_OVER_MEMORY
explicit ZlibCompressorIStreambuf(
std::istream& iStream,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0);
// Alternate constructor with sink semantics for the “source” std::istream.
explicit ZlibCompressorIStreambuf(
std::unique_ptr<std::istream> _iStream_p,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0);
ZlibCompressorIStreambuf(const ZlibCompressorIStreambuf&) = delete;
ZlibCompressorIStreambuf& operator=(const ZlibCompressorIStreambuf&) = delete;
virtual ~ZlibCompressorIStreambuf();
~ZlibCompressorIStreambuf();
protected:
virtual OperationType operationType() const override;
OperationType operationType() const override;
// Initialize the z_stream struct used by zlib
void zStreamInit(int compressionLevel, ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy);
// Call zlib's deflate() function to compress data.
virtual int zlibProcessData() override;
int zlibProcessData() override;
};
@@ -345,39 +307,27 @@ class ZlibDecompressorIStreambuf: public ZlibAbstractIStreambuf
public:
// Same parameters as for ZlibAbstractIStreambuf, except:
//
// format ZLibCompressionFormat::ZLIB,
// ZLibCompressionFormat::GZIP or
// ZLibCompressionFormat::AUTODETECT
// format ZLIB_COMPRESSION_FORMAT_ZLIB,
// ZLIB_COMPRESSION_FORMAT_GZIP or
// ZLIB_COMPRESSION_FORMAT_AUTODETECT
explicit ZlibDecompressorIStreambuf(
std::istream& iStream,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
// Alternate constructor with sink semantics for the “source” std::istream.
explicit ZlibDecompressorIStreambuf(
std::unique_ptr<std::istream> _iStream_p,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibDecompressorIStreambuf(const ZlibDecompressorIStreambuf&) = delete;
ZlibDecompressorIStreambuf& operator=(const ZlibDecompressorIStreambuf&)
= delete;
virtual ~ZlibDecompressorIStreambuf();
~ZlibDecompressorIStreambuf();
protected:
virtual OperationType operationType() const override;
OperationType operationType() const override;
void zStreamInit(ZLibCompressionFormat format);
virtual int zlibProcessData() override;
int zlibProcessData() override;
};
// std::istream subclass for compressing data. Input data is obtained from an
@@ -399,30 +349,16 @@ public:
std::istream& iStream,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
// Alternate constructor with sink semantics for the “source” std::istream.
explicit ZlibCompressorIStream(
std::unique_ptr<std::istream> _iStream_p,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibCompressorIStream(const ZlibCompressorIStream&) = delete;
ZlibCompressorIStream& operator=(const ZlibCompressorIStream&) = delete;
virtual ~ZlibCompressorIStream();
~ZlibCompressorIStream();
private:
ZlibCompressorIStreambuf _streamBuf;
@@ -446,27 +382,15 @@ public:
explicit ZlibDecompressorIStream(
std::istream& iStream,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
// Alternate constructor with sink semantics for the “source” std::istream.
explicit ZlibDecompressorIStream(
std::unique_ptr<std::istream> _iStream_p,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibDecompressorIStream(const ZlibDecompressorIStream&) = delete;
ZlibDecompressorIStream& operator=(const ZlibDecompressorIStream&) = delete;
virtual ~ZlibDecompressorIStream();
~ZlibDecompressorIStream();
private:
ZlibDecompressorIStreambuf _streamBuf;

View File

@@ -4,39 +4,36 @@
//
// 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 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 distributed in the hope that it will be useful,
// 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
// Library General Public License for more details.
// 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 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>
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <ios> // std::basic_ios, std::streamsize...
#include <iostream> // std::ios_base, std::cerr, etc.
#include <sstream>
#include <array>
#include <random>
#include <memory> // std::unique_ptr
#include <utility> // std::move()
#include <limits> // std::numeric_limits
#include <type_traits> // std::make_unsigned()
#include <functional> // std::bind()
#include <cassert>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <cstring> // strcmp()
#include <zlib.h> // Z_BEST_COMPRESSION
using std::string;
using std::cout;
using std::cerr;
using traits = std::char_traits<char>;
#include <simgear/misc/test_macros.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
@@ -44,24 +41,6 @@
#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
@@ -69,8 +48,8 @@ static std::size_t streamsizeToSize_t(std::streamsize n)
// you don't need the putback feature in non-test code, best performance is
// achieved with putback size = 0.
//
// I suggest reading test_IStreamConstructorWithSinkSemantics() below to see
// how to use the classes efficiently.
// I suggest you read roundTripWithIStreams() below to see how to use the
// classes most efficiently (especially the comments!).
static std::default_random_engine randomNumbersGenerator;
@@ -198,9 +177,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::ZLibCompressionFormat::ZLIB,
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
nullptr, compInBufSize, nullptr, compOutBufSize, compPutbackSize);
text_ss, SGPath(), 8, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY, nullptr, compInBufSize, nullptr,
compOutBufSize, compPutbackSize);
std::stringstream compressedOutput_ss;
compressedOutput_ss << &compSBuf;
@@ -208,7 +187,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::ZLibCompressionFormat::ZLIB,
compressedOutput_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
int ch = decompSBuf.sgetc();
@@ -235,9 +214,7 @@ void test_StreambufBasicOperations()
// Most efficient way (with the underlying xsgetn()) to read several chars
// at once.
std::streamsize n = decompSBuf.sgetn(buf, bufSize);
SG_CHECK_EQUAL(n, bufSize);
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(bufSize)),
"3456789abc");
SG_VERIFY(n == bufSize && string(buf, bufSize) == "3456789abc");
ch = decompSBuf.sungetc(); // same as sputbackc(), except no value to check
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
@@ -249,17 +226,15 @@ void test_StreambufBasicOperations()
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
n = decompSBuf.sgetn(buf, bufSize);
SG_CHECK_EQUAL(n, bufSize);
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(bufSize)),
"bcdefghijk");
SG_VERIFY(n == bufSize && string(buf, bufSize) == "bcdefghijk");
ch = decompSBuf.sungetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
static char buf2[64];
n = decompSBuf.sgetn(buf2, sizeof(buf2));
SG_CHECK_EQUAL(n, 36);
SG_CHECK_EQUAL(string(buf2, 36), "klmnopqrstuvwxyz\nABCDEF\nGHIJK LMNOPQ");
SG_VERIFY(n == 36 && string(buf2, n) == "klmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ");
ch = decompSBuf.sbumpc();
SG_CHECK_EQUAL(ch, EOF);
@@ -293,60 +268,6 @@ 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";
@@ -354,12 +275,12 @@ void test_formattedInputFromDecompressor()
static char inBuf[6];
static char outBuf[15];
string compressed = compress(
lipsum, simgear::ZLibCompressionFormat::ZLIB, Z_BEST_COMPRESSION,
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
lipsum, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB, Z_BEST_COMPRESSION,
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
std::istringstream compressed_ss(compressed);
simgear::ZlibDecompressorIStream decompressor(
compressed_ss, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
compressed_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), /* putback size */ 1);
decompressor.exceptions(std::ios_base::badbit); // throw if badbit is set
@@ -398,15 +319,15 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
simgear::ZlibCompressorIStream compressor(
text_ss, SGPath(), Z_BEST_COMPRESSION,
simgear::ZLibCompressionFormat::ZLIB,
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
compInBuf, sizeof(compInBuf), compOutBuf, sizeof(compOutBuf),
/* putback size */ 0);
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
// Use the compressor (subclass of std::istream) as input to the decompressor
simgear::ZlibDecompressorIStream decompressor(
compressor, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
compressor, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
decompInBuf, sizeof(decompInBuf), decompOutBuf, sizeof(decompOutBuf),
/* putback size */ 3);
decompressor.exceptions(std::ios_base::badbit);
@@ -433,14 +354,6 @@ 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());
}
@@ -491,10 +404,7 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
string rest(buf2, nbCharsRead);
do {
decompressor.read(buf2, sizeof(buf2));
// The conversion to std::size_t is safe because decompressor.read()
// returns a non-negative value which, in this case, can't exceed
// sizeof(buf2).
rest += string(buf2, streamsizeToSize_t(decompressor.gcount()));
rest += string(buf2, decompressor.gcount());
} while (decompressor);
SG_CHECK_EQUAL(rest, " LMNOPQ");
@@ -507,6 +417,7 @@ 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
@@ -531,7 +442,7 @@ void roundTripWithIStreams(
{
const simgear::ZLibCompressionFormat decompFormat =
(useAutoFormatForDecompression) ?
simgear::ZLibCompressionFormat::AUTODETECT : compressionFormat;
simgear::ZLIB_COMPRESSION_FORMAT_AUTODETECT : compressionFormat;
std::istringstream lipsum_ss(lipsum);
// This tests the optional dynamic buffer allocation in ZlibAbstractIStreambuf
@@ -571,12 +482,11 @@ void test_RoundTripMultiWithIStreams()
const std::size_t compPutbackSize = 1;
const std::size_t decompPutbackSize = 1;
for (auto format: {simgear::ZLibCompressionFormat::ZLIB,
simgear::ZLibCompressionFormat::GZIP}) {
for (auto format: {simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_COMPRESSION_FORMAT_GZIP}) {
for (int compressionLevel: {1, 4, 7, 9}) {
for (auto memStrategy: {
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY}) {
for (auto memStrategy: {simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY}) {
for (std::size_t compInBufSize: {3, 4}) {
for (std::size_t compOutBufSize: {3, 5}) {
for (std::size_t decompInBufSize: {3, 4}) {
@@ -595,10 +505,9 @@ void test_RoundTripMultiWithIStreams()
}
{
const auto format = simgear::ZLibCompressionFormat::ZLIB;
const auto format = simgear::ZLIB_COMPRESSION_FORMAT_ZLIB;
const int compressionLevel = Z_DEFAULT_COMPRESSION;
const auto memStrategy =
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY;
const auto memStrategy = simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY;
for (std::size_t compInBufSize: {3, 4, 31, 256, 19475}) {
for (std::size_t compOutBufSize: {3, 5, 9, 74, 4568}) {
@@ -628,12 +537,11 @@ 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::ZLibCompressionFormat::ZLIB :
simgear::ZLibCompressionFormat::GZIP;
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB :
simgear::ZLIB_COMPRESSION_FORMAT_GZIP;
roundTripWithIStreams(
compFormat, Z_BEST_COMPRESSION,
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
compFormat, Z_BEST_COMPRESSION, simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
compInBufSize, compOutBufSize, decompInBufSize, decompOutBufSize,
compPutbackSize, decompPutbackSize,
/* automatic format detection for decompression */ true);
@@ -642,91 +550,13 @@ 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;
}

View File

@@ -20,7 +20,7 @@
//
// $Id$
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <string>

View File

@@ -23,7 +23,6 @@
$Id: netBuffer.cxx 1568 2002-09-02 06:05:49Z sjbaker $
*/
#include <simgear_config.h>
#include "sg_netBuffer.hxx"
#include <cassert>

View File

@@ -29,7 +29,6 @@
// to write or something...]
// Maybe assert valid handle, too?
#include <simgear_config.h>
#include "sg_netChannel.hxx"
#include <memory>

View File

@@ -20,7 +20,6 @@
//
// $Id$
#include <simgear_config.h>
#include <cstdlib>
#include <cstring>

View File

@@ -78,9 +78,6 @@ 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);
@@ -101,7 +98,8 @@ int main(int argc, char* argv[])
cout << "done" << endl;
}
cout << "test existing NAPTR: " << EXISTING_RECORD << endl;
#define EXISTING_RECORD "terrasync.flightgear.org"
cout << "test existing NAPTR: " EXISTING_RECORD << endl;
{
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
DNS::Request_ptr r(naptrRequest);
@@ -112,24 +110,23 @@ int main(int argc, char* argv[])
}
if( r->isTimeout() ) {
cerr << "timeout testing existing record " << EXISTING_RECORD << endl;
cerr << "timeout testing existing record " EXISTING_RECORD << endl;
return EXIT_FAILURE;
}
if(naptrRequest->entries.empty()) {
cerr << "no results for " << EXISTING_RECORD << endl;
cerr << "no results for " EXISTING_RECORD << endl;
return EXIT_FAILURE;
}
cout << "test for ascending preference/order" << endl;
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, disable temporarily
//SG_CHECK_EQUAL((*it)->service, "ws20" );
// currently only support ws20
SG_CHECK_EQUAL((*it)->service, "ws20" );
if( (*it)->order < order ) {
cerr << "NAPTR entries not ascending for field 'order'" << endl;
@@ -157,29 +154,6 @@ 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;
{

View File

@@ -2,7 +2,6 @@
// Test harness.
////////////////////////////////////////////////////////////////////////
#include <simgear_config.h>
#include <simgear/compiler.h>
#include <iostream>

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "untar.hxx"
#include <cstdlib>

View File

@@ -341,16 +341,18 @@ private:
};
public:
simd4_t(void) { simd4 = _mm_setzero_ps(); }
simd4_t(float f) { simd4 = _mm_set1_ps(f); }
simd4_t(void) : simd4(_mm_setzero_ps()) {}
simd4_t(float f) {}
simd4_t(float x, float y) : simd4_t(x,y,0,0) {}
simd4_t(float x, float y, float z) : simd4_t(x,y,z,0) {}
simd4_t(float x, float y, float z, float w) { 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; }
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;
}
inline const __m128 (&v4(void) const) {
return simd4;
@@ -581,18 +583,18 @@ private:
};
public:
simd4_t(void) { simd4 = _mm256_setzero_pd(); }
simd4_t(double d) { simd4 = _mm256_set1_pd(d); }
simd4_t(void) : simd4(_mm256_setzero_pd()) {}
simd4_t(double d) {}
simd4_t(double x, double y) : simd4_t(x,y,0,0) {}
simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {}
simd4_t(double x, double y, double z, double w) {
simd4 = _mm256_set_pd(w,z,y,x);
simd4_t(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(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;
@@ -817,24 +819,14 @@ private:
public:
simd4_t(void) { simd4[0] = simd4[1] = _mm_setzero_pd(); }
simd4_t(double d) { simd4[0] = simd4[1] = _mm_set1_pd(d); }
simd4_t(double d) {}
simd4_t(double x, double y) : simd4_t(x,y,0,0) {}
simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {}
simd4_t(double x, double y, double z, double w) {
simd4[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(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 __m128d v[2]) {
simd4[0] = v[0];
simd4[1] = v[1];
@@ -1109,16 +1101,18 @@ private:
};
public:
simd4_t(void) { simd4 = _mm_setzero_si128(); }
simd4_t(int i) { simd4 = _mm_set1_epi32(i); }
simd4_t(void) : simd4(_mm_setzero_si128()) {}
simd4_t(int i) {}
simd4_t(int x, int y) : simd4_t(x,y,0,0) {}
simd4_t(int x, int y, int z) : simd4_t(x,y,z,0) {}
simd4_t(int x, int y, int z, int w) { 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; }
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;
}
inline __m128i (&v4(void)) {
return simd4;

View File

@@ -7,7 +7,6 @@ set(HEADERS
ResourceManager.hxx
SimpleMarkdown.hxx
SVGpreserveAspectRatio.hxx
argparse.hxx
interpolator.hxx
make_new.hxx
sg_dir.hxx
@@ -18,7 +17,6 @@ set(HEADERS
strutils.hxx
tabbed_values.hxx
texcoord.hxx
test_macros.hxx
)
set(SOURCES
@@ -26,7 +24,6 @@ set(SOURCES
ResourceManager.cxx
SimpleMarkdown.cxx
SVGpreserveAspectRatio.cxx
argparse.cxx
interpolator.cxx
sg_dir.cxx
sg_path.cxx
@@ -48,10 +45,6 @@ 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})

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "SVGpreserveAspectRatio.hxx"
#include <simgear/debug/logstream.hxx>

View File

@@ -1,391 +0,0 @@
// -*- 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

View File

@@ -1,281 +0,0 @@
// -*- 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_

View File

@@ -1,621 +0,0 @@
// -*- 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;
}

View File

@@ -1,4 +1,3 @@
#include <simgear_config.h>
#include <simgear/compiler.h>

View File

@@ -1,5 +1,3 @@
#include <simgear_config.h>
#include <cstdlib>
#include <simgear/misc/sg_path.hxx>

View File

@@ -53,9 +53,9 @@ static const char sgDirPathSep = '/';
static const char sgDirPathSepBad = '\\';
#ifdef _WIN32
const char SGPath::pathListSep[] = ";"; // this is null-terminated
const char SGPath::pathListSep = ';';
#else
const char SGPath::pathListSep[] = ":"; // ditto
const char SGPath::pathListSep = ':';
#endif
#ifdef _WIN32
@@ -278,6 +278,7 @@ SGPath& SGPath::operator=(const SGPath& p)
SGPath::~SGPath() {
}
#if defined(ENABLE_OLD_PATH_API)
// set path
void SGPath::set( const string& p ) {
path = p;
@@ -285,6 +286,7 @@ void SGPath::set( const string& p ) {
_cached = false;
_rwCached = false;
}
#endif
//------------------------------------------------------------------------------
void SGPath::setPermissionChecker(PermissionChecker validator)
@@ -332,7 +334,7 @@ SGPath SGPath::operator/( const std::string& p ) const
#if defined(ENABLE_OLD_PATH_API)
//add a new path component to the existing path string
void SGPath::add( const string& p ) {
append( SGPath::pathListSep[0] + p );
append( SGPath::pathListSep+p );
}
#endif
@@ -532,11 +534,10 @@ void SGPath::validate() const
//------------------------------------------------------------------------------
void SGPath::checkAccess() const
{
if ( _rwCached && _cacheEnabled ) {
if( _rwCached && _cacheEnabled )
return;
}
validate();
validate();
_rwCached = true;
}
@@ -665,7 +666,7 @@ string_list sgPathSplit( const string &search_path ) {
bool done = false;
while ( !done ) {
int index = tmp.find(SGPath::pathListSep[0]);
int index = tmp.find(SGPath::pathListSep);
if (index >= 0) {
result.push_back( tmp.substr(0, index) );
tmp = tmp.substr( index + 1 );

View File

@@ -52,8 +52,8 @@ class SGPath {
public:
// OS-dependent separator used in paths lists (C-style string of length 1)
static const char pathListSep[2];
// OS-dependent separator used in paths lists
static const char pathListSep;
struct Permissions
{

View File

@@ -20,15 +20,12 @@
//
// $Id$
#include <simgear_config.h>
#include <string>
#include <ctype.h>
#include <cstring>
#include <sstream>
#include <algorithm>
#include <type_traits>
#include <cstring> // strerror_r() and strerror_s()
#include <cctype>
#include <cerrno>
#include <string.h> // strerror_r() and strerror_s()
#include <errno.h>
#if defined(HAVE_CPP11_CODECVT)
#include <codecvt> // new in C++11
@@ -375,170 +372,6 @@ 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, "."));
@@ -797,59 +630,6 @@ 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)
{
@@ -885,17 +665,16 @@ std::string unescape(const char* s)
if (!*++s)
break;
int v = 0;
for (/* empty */; isxdigit(*s); s++) {
for (int i = 0; i < 2 && isxdigit(*s); i++, s++)
v = v * 16 + (isdigit(*s) ? *s - '0' : 10 + tolower(*s) - 'a');
}
r += static_cast<char>(v);
r += v;
continue;
} else if (*s >= '0' && *s <= '7') {
int v = *s++ - '0';
for (int i = 0; i < 2 && *s >= '0' && *s <= '7'; i++, s++)
for (int i = 0; i < 3 && *s >= '0' && *s <= '7'; i++, s++)
v = v * 8 + *s - '0';
r += static_cast<char>(v);
r += v;
continue;
} else {
@@ -960,94 +739,6 @@ 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

View File

@@ -31,7 +31,6 @@
#include <string>
#include <vector>
#include <type_traits>
#include <cstdlib>
typedef std::vector < std::string > string_list;
@@ -169,52 +168,7 @@ 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.
@@ -269,43 +223,26 @@ 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
@@ -321,20 +258,6 @@ 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

View File

@@ -1,27 +1,15 @@
// -*- 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;
@@ -97,290 +85,6 @@ 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");
@@ -435,72 +139,9 @@ 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()
@@ -534,32 +175,6 @@ 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)
@@ -590,15 +205,11 @@ 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;
}

View File

@@ -15,7 +15,6 @@
// 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>

View File

@@ -167,14 +167,16 @@ int parseTest()
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-panel.png");
SG_VERIFY(index->type == pkg::Package::Preview::Type::PANEL);
// 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");
// old-style thumbnails
string_list oldThumbUrls = p2->thumbnailUrls();
SG_CHECK_EQUAL(oldThumbUrls.size(), 1);
SG_CHECK_EQUAL(oldThumbUrls.at(0), "http://foo.bar.com/thumb-exterior.png");
string_list oldThumbPaths = p2->thumbnails();
SG_CHECK_EQUAL(oldThumbPaths.size(), 1);
SG_CHECK_EQUAL(oldThumbPaths.at(0), "exterior.png");
// test variants
SG_CHECK_EQUAL(p2->parentIdForVariant(0), std::string());
try {
p2->indexOfVariant("fofofo");
SG_TEST_FAIL("lookup of non-existant variant did not throw");
@@ -188,8 +190,6 @@ 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,7 +199,6 @@ 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");
@@ -216,22 +215,6 @@ 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());

View File

@@ -15,7 +15,6 @@
// 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>
@@ -456,6 +455,33 @@ 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);

View File

@@ -98,6 +98,13 @@ 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.

View File

@@ -15,8 +15,6 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include <simgear/package/Package.hxx>
#include <cassert>
@@ -276,12 +274,28 @@ SGPropertyNode* Package::properties() const
string_list Package::thumbnailUrls() const
{
string_list urls;
const Thumbnail& thumb(thumbnailForVariant(0));
if (!thumb.url.empty()) {
urls.push_back(thumb.url);
string_list r;
if (!m_props) {
return r;
}
return urls;
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;
}
string_list Package::downloadUrls() const
@@ -291,7 +305,7 @@ string_list Package::downloadUrls() const
return r;
}
for (auto dl : m_props->getChildren("url")) {
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
r.push_back(dl->getStringValue());
}
return r;
@@ -412,49 +426,7 @@ SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const cha
return m_props;
}
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")};
throw sg_exception("Unknow variant in package " + id());
}
Package::PreviewVec Package::previewsForVariant(unsigned int vIndex) const

View File

@@ -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,25 +62,15 @@ public:
existingInstall(const InstallCallback& cb = InstallCallback()) const;
bool isInstalled() const;
/**
* package ID
*/
std::string id() const;
/**
* Variant IDs
* Variant IDs. Note the primary ID will always be included as
* variants()[0], to simplify enumerating all variants
*/
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
*/
@@ -99,10 +89,8 @@ public:
/**
* human-readable name - note this is probably not localised,
* although this is not ruled out for the future.
*
* Deprecated - please use nameForVariant
*/
SG_DEPRECATED(std::string name() const);
std::string name() const;
/**
* Human readable name of a variant
@@ -113,31 +101,28 @@ public:
/**
* syntactic sugar to get the localised description
*
* Deprecated - please use getLocalisedProp to get the variant-specific
* description.
*/
SG_DEPRECATED(std::string description() const);
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;
@@ -146,15 +131,13 @@ public:
* download URLs for the package
*/
string_list downloadUrls() const;
string_list thumbnailUrls() const;
struct Thumbnail
{
std::string url;
std::string path;
};
Thumbnail thumbnailForVariant(unsigned int vIndex) const;
/**
* thumbnail file paths within the package on disk
*/
string_list thumbnails() const;
/**
* information about a preview image
@@ -184,7 +167,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,
@@ -197,23 +180,14 @@ 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);
@@ -229,7 +203,7 @@ private:
string_set m_tags;
CatalogRef m_catalog;
string_list m_variants;
mutable function_list<InstallCallback> _install_cb;
};
@@ -241,3 +215,4 @@ private:
} // of namespace simgear
#endif // of SG_PACKAGE_PACKAGE_HXX

View File

@@ -15,8 +15,6 @@
// 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>

View File

@@ -89,9 +89,6 @@
<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>
@@ -99,8 +96,6 @@
<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>
@@ -114,33 +109,10 @@
</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>

View File

@@ -15,8 +15,6 @@
// 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>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "PropertyBasedElement.hxx"
#include <boost/algorithm/string/predicate.hpp>

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "PropertyBasedMgr.hxx"
#include <boost/foreach.hpp>

View File

@@ -16,7 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "PropertyInterpolationMgr.hxx"
#include "PropertyInterpolator.hxx"
#include "props.hxx"

View File

@@ -16,8 +16,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear_config.h>
#include "PropertyInterpolator.hxx"
#include "props.hxx"

View File

@@ -602,45 +602,6 @@ 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.
*

View File

@@ -15,6 +15,3 @@ foreach( mylibfolder
endforeach( mylibfolder )
if(ENABLE_GDAL)
add_subdirectory(dem)
endif(ENABLE_GDAL)

View File

@@ -1,24 +0,0 @@
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}")

View File

@@ -1,418 +0,0 @@
// 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");

View File

@@ -1,53 +0,0 @@
// 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

View File

@@ -1,332 +0,0 @@
// 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

View File

@@ -1,82 +0,0 @@
// 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__ */

View File

@@ -1,72 +0,0 @@
#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" );
}

View File

@@ -1,90 +0,0 @@
#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__ */

View File

@@ -1,118 +0,0 @@
#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;
}

View File

@@ -1,102 +0,0 @@
#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