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
182 changed files with 1163 additions and 11304 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)
@@ -249,8 +247,8 @@ else()
# declaring symbols as declspec(import)
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
set(EXPAT_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
${PROJECT_SOURCE_DIR}/3rdparty/expat
${PROJECT_BINARY_DIR}/3rdparty/expat)
endif(SYSTEM_EXPAT)
check_include_file(inttypes.h HAVE_INTTYPES_H)
@@ -264,28 +262,13 @@ if(HAVE_INTTYPES_H)
endif()
if(ENABLE_RTI)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
pkg_check_modules(RTI hla-rti13)
endif(PKG_CONFIG_FOUND)
if(RTI_FOUND)
SET(RTI_INCLUDE_DIR "${RTI_INCLUDE_DIRS}")
message(STATUS "RTI: ENABLED")
else()
message(STATUS "RTI: DISABLED")
endif(RTI_FOUND)
# See if we have any rti library variant installed
message(STATUS "RTI: ENABLED")
find_package(RTI)
else()
message(STATUS "RTI: DISABLED")
endif(ENABLE_RTI)
if(ENABLE_GDAL)
find_package(GDAL 2.0.0 REQUIRED)
if (GDAL_FOUND)
include_directories(${GDAL_INCLUDE_DIR})
endif(GDAL_FOUND)
endif(ENABLE_GDAL)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists(rint HAVE_RINT)
check_function_exists(mkdtemp HAVE_MKDTEMP)
@@ -352,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")
@@ -380,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++")
@@ -398,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)
@@ -494,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)

76
SimGear.spec.in Normal file
View File

@@ -0,0 +1,76 @@
%define ver @VERSION@
%define rel 1
%define prefix /usr
Summary: Simulator Construction Gear.
Name: @PACKAGE@
Version: %ver
Release: %rel
Copyright: LGPL
Group: Libraries/Graphics
Source: %{name}-%{version}.tar.gz
#URL:
BuildRoot: /tmp/%{name}-%{version}-%{rel}-root
Packager: Fill In As You Wish
Docdir: %{prefix}/doc
%description
This package contains a tools and libraries useful for constructing
simulation and visualization applications such as FlightGear or TerraGear.
Authors:
N/A
%prep
%setup -n %{name}-%{version}
%build
# Needed for snapshot releases.
if [ ! -f configure ]; then
CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%prefix
else
CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%prefix
fi
if [ "$SMP" != "" ]; then
JSMP = '"MAKE=make -k -j $SMP"'
fi
make ${JSMP};
%install
[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
make prefix=${RPM_BUILD_ROOT}%{prefix} install
#
# Generating file lists and store them in file-lists
# Starting with the directory listings
#
find ${RPM_BUILD_ROOT}%{prefix}/{bin,include,lib} -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" > file-lists
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" >> file-lists}
#
# Then, the file listings
#
echo "%defattr (-, root, root)" >> file-lists
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc/%{name}.conf -type f | sed -e "s#^${RPM_BUILD_ROOT}#%config #g" >> file-lists}
find ${RPM_BUILD_ROOT}%{prefix} -type f | sed -e "s#^${RPM_BUILD_ROOT}##g" >> file-lists
%clean
(cd ..; rm -rf %{name}-%{version} ${RPM_BUILD_ROOT})
%files -f file-lists
%defattr (-, root, root)
%doc AUTHORS
%doc COPYING
%doc ChangeLog
%doc INSTALL
%doc NEWS
%doc README
%doc %{name}.spec.in

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

@@ -5,7 +5,6 @@ foreach( mylibfolder
bucket
bvh
debug
embedded_resources
ephemeris
io
magvar
@@ -173,11 +172,6 @@ if(NOT SIMGEAR_HEADLESS)
${OPENGL_LIBRARY}
${JPEG_LIBRARY})
if(ENABLE_GDAL)
target_link_libraries(SimGearScene
${GDAL_LIBRARIES})
endif()
# only actually needed by canvas/KeyboardEvent.cxx
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
endif()

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, unsigned int columns)
{
unsigned int i, j;
char temp[3000], temp1[3000];
*temp = 0;
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
{
if (strlen(temp) > 500) return;
/* print offset */
if (i % columns == 0)
{
sprintf(temp1, "0x%06x: ", i);
strcat(temp, temp1);
}
/* print hex data */
if (i < len)
{
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
strcat(temp, temp1);
}
else /* end of block, just aligning for ASCII dump */
{
strcat(temp, " ");
}
/* print ASCII dump */
if (i % columns == (columns - 1))
{
for (j = i - (columns - 1); j <= i; j++)
{
if (j >= len) /* end of block, not really printing */
{
strcat(temp, " ");
}
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
{
char t2[2];
t2[0] = 0xFF & ((char*)mem)[j];
t2[1] = 0;
strcat(temp, t2);
}
else /* other char */
{
strcat(temp, ".");
}
}
log(c, p, fileName, line, temp );
*temp = 0;
}
}
global_privateLogstream->log(c, p, fileName, line, msg);
}
void
@@ -612,31 +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, unsigned 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

@@ -1,14 +0,0 @@
include (SimGearComponent)
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx)
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx)
simgear_component(embedded_resources embedded_resources
"${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_embedded_resources embedded_resources_test.cxx)
target_link_libraries(test_embedded_resources ${TEST_LIBS})
add_test(embedded_resources
${EXECUTABLE_OUTPUT_PATH}/test_embedded_resources)
endif(ENABLE_TESTS)

View File

@@ -1,265 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResource.cxx --- Class for pointing to/accessing an embedded resource
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <string>
#include <iosfwd>
#include <ios> // std::streamsize
#include <ostream>
#include <memory> // std::unique_ptr
#include <utility> // std::move()
#include <algorithm> // std::min()
#include <limits> // std::numeric_limits
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include "EmbeddedResource.hxx"
using std::string;
using std::unique_ptr;
// Inspired by <http://stackoverflow.com/a/21174979/4756009>
template<typename Derived, typename Base>
static unique_ptr<Derived> static_unique_ptr_cast(unique_ptr<Base> p)
{
auto d = static_cast<Derived *>(p.release());
return unique_ptr<Derived>(d);
}
namespace simgear
{
// ***************************************************************************
// * AbstractEmbeddedResource class *
// ***************************************************************************
AbstractEmbeddedResource::AbstractEmbeddedResource(const char *data,
std::size_t size)
: _data(data),
_size(size)
{ }
const char *AbstractEmbeddedResource::rawPtr() const
{
return _data;
}
std::size_t AbstractEmbeddedResource::rawSize() const
{
return _size;
}
string AbstractEmbeddedResource::str() const
{
if (_size > std::numeric_limits<string::size_type>::max()) {
throw sg_range_exception(
"Resource too large to fit in an std::string (size: " +
std::to_string(_size) + " bytes)");
}
return string(_data, _size);
}
// ***************************************************************************
// * RawEmbeddedResource class *
// ***************************************************************************
RawEmbeddedResource::RawEmbeddedResource(const char *data, std::size_t size)
: AbstractEmbeddedResource(data, size)
{ }
AbstractEmbeddedResource::CompressionType
RawEmbeddedResource::compressionType() const
{
return AbstractEmbeddedResource::CompressionType::NONE;
}
string RawEmbeddedResource::compressionDescr() const
{
return string("none");
}
unique_ptr<std::streambuf> RawEmbeddedResource::streambuf() const
{
// This is a read-only variant of CharArrayStreambuf
return unique_ptr<std::streambuf>(
new ROCharArrayStreambuf(rawPtr(), rawSize()));
}
unique_ptr<std::istream> RawEmbeddedResource::istream() const
{
return unique_ptr<std::istream>(new CharArrayIStream(rawPtr(), rawSize()));
}
// ***************************************************************************
// * ZlibEmbeddedResource class *
// ***************************************************************************
ZlibEmbeddedResource::ZlibEmbeddedResource(const char *data,
std::size_t compressedSize,
std::size_t uncompressedSize)
: AbstractEmbeddedResource(data, compressedSize),
_uncompressedSize(uncompressedSize),
_inBuf(nullptr),
_inBufSize(262144), // adjusted below in the constructor body
_outBuf(nullptr),
_outBufSize(262144),
_putbackSize(0) // default for best performance
{
static_assert(262144 <= std::numeric_limits<std::size_t>::max(),
"The std::size_t type is unexpectedly small.");
// No need to use an input buffer (where compressed data chunks are put for
// zlib to read and decompress) larger than the whole compressed resource!
_inBufSize = std::min(rawSize(), _inBufSize);
}
AbstractEmbeddedResource::CompressionType
ZlibEmbeddedResource::compressionType() const
{ return AbstractEmbeddedResource::CompressionType::ZLIB; }
string ZlibEmbeddedResource::compressionDescr() const
{ return string("zlib"); }
std::size_t ZlibEmbeddedResource::uncompressedSize() const
{ return _uncompressedSize; }
char* ZlibEmbeddedResource::getInputBufferStart()
{ return _inBuf; }
void ZlibEmbeddedResource::setInputBufferStart(char* inBuf)
{ _inBuf = inBuf; }
std::size_t ZlibEmbeddedResource::getInputBufferSize()
{ return _inBufSize; }
void ZlibEmbeddedResource::setInputBufferSize(std::size_t size)
{ _inBufSize = size; }
char* ZlibEmbeddedResource::getOutputBufferStart()
{ return _outBuf; }
void ZlibEmbeddedResource::setOutputBufferStart(char* outBuf)
{ _outBuf = outBuf; }
std::size_t ZlibEmbeddedResource::getOutputBufferSize()
{ return _outBufSize; }
void ZlibEmbeddedResource::setOutputBufferSize(std::size_t size)
{ _outBufSize = size; }
std::size_t ZlibEmbeddedResource::getPutbackSize()
{ return _putbackSize; }
void ZlibEmbeddedResource::setPutbackSize(std::size_t size)
{ _putbackSize = size; }
unique_ptr<std::streambuf> ZlibEmbeddedResource::streambuf() const
{
unique_ptr<CharArrayIStream> rawReaderIStream(
new CharArrayIStream(rawPtr(), rawSize()));
return unique_ptr<std::streambuf>(
new ZlibDecompressorIStreambuf(
std::move(rawReaderIStream),
SGPath(), // rawReaderIStream isn't bound to a file
ZLibCompressionFormat::ZLIB,
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
}
unique_ptr<std::istream> ZlibEmbeddedResource::istream() const
{
unique_ptr<CharArrayIStream> rawReaderIStream(
new CharArrayIStream(rawPtr(), rawSize()));
return unique_ptr<std::istream>(
new ZlibDecompressorIStream(
std::move(rawReaderIStream),
SGPath(), // rawReaderIStream isn't bound to a file
ZLibCompressionFormat::ZLIB,
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
}
std::string ZlibEmbeddedResource::str() const
{
static constexpr std::size_t bufSize = 65536;
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
"Type std::streamsize is unexpectedly small");
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
"Type std::string::size_type is unexpectedly small");
unique_ptr<char[]> buf(new char[bufSize]);
auto decompressor =
static_unique_ptr_cast<ZlibDecompressorIStream>(istream());
std::streamsize nbCharsRead;
string result;
if (_uncompressedSize > std::numeric_limits<string::size_type>::max()) {
throw sg_range_exception(
"Resource too large to fit in an std::string (uncompressed size: "
+ std::to_string(_uncompressedSize) + " bytes)");
} else {
result.reserve(static_cast<string::size_type>(_uncompressedSize));
}
do {
decompressor->read(buf.get(), bufSize);
nbCharsRead = decompressor->gcount();
if (nbCharsRead > 0) {
result.append(buf.get(), nbCharsRead);
}
} while (*decompressor);
// decompressor->fail() would *not* indicate an error, due to the semantics
// of std::istream::read().
if (decompressor->bad()) {
throw sg_io_exception("Error while extracting a compressed resource");
}
return result;
}
// ***************************************************************************
// * Stream insertion operators *
// ***************************************************************************
std::ostream& operator<<(std::ostream& os,
const RawEmbeddedResource& resource)
{ // This won't escape double quotes, backslashes, etc. in resource.str().
return os << "RawEmbeddedResource:\n"
" compressionType = \"" << resource.compressionDescr() << "\"\n"
" rawPtr = " << (void*) resource.rawPtr() << "\n"
" rawSize = " << resource.rawSize();
}
std::ostream& operator<<(std::ostream& os,
const ZlibEmbeddedResource& resource)
{ // This won't escape double quotes, backslashes, etc. in resource.str().
return os << "ZlibEmbeddedResource:\n"
" compressionType = \"" << resource.compressionDescr() << "\"\n"
" rawPtr = " << (void*) resource.rawPtr() << "\n"
" rawSize = " << resource.rawSize() << "\n"
" uncompressedSize = " << resource.uncompressedSize();
}
} // of namespace simgear

View File

@@ -1,163 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResource.hxx --- Class for pointing to/accessing an embedded resource
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCE_HXX
#define FG_EMBEDDEDRESOURCE_HXX
#include <iosfwd>
#include <string>
#include <ostream>
#include <memory> // std::unique_ptr
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <simgear/io/iostreams/zlibstream.hxx>
namespace simgear
{
// Abstract base class for embedded resources
class AbstractEmbeddedResource
{
public:
enum class CompressionType {
NONE = 0,
ZLIB
};
// Constructor.
//
// 'data' and 'size' indicate the resource contents. There is no requirement
// of null-termination, including for text data (given how
// EmbeddedResourceManager::getString() works, including a null terminator
// for text contents is actually counter-productive). The data may be of
// arbitrary type and size: binary, text, whatever. The constructed object
// (for derived classes since this one is abstract) does *not* hold a copy
// of the data, it just keeps a pointer to it and provides methods to access
// it. The data must therefore remain available as long as the object is in
// use---this class was designed for use with data stored in static
// variables.
explicit AbstractEmbeddedResource(const char *data, std::size_t size);
AbstractEmbeddedResource(const AbstractEmbeddedResource&) = default;
AbstractEmbeddedResource(AbstractEmbeddedResource&&) = default;
AbstractEmbeddedResource& operator=(const AbstractEmbeddedResource&) = default;
AbstractEmbeddedResource& operator=(AbstractEmbeddedResource&&) = default;
virtual ~AbstractEmbeddedResource() = default;
// Return the pointer to beginning-of-resource contents---the same that was
// passed to the constructor.
const char *rawPtr() const;
// Return the resource size, as passed to the constructor. For a compressed
// resource, this is the compressed size; such resources provide an
// additional uncompressedSize() method.
std::size_t rawSize() const;
// Return an std::string object containing a copy of the resource contents.
// For a compressed resource, this is the data obtained after decompression.
virtual std::string str() const;
// Return an std::streambuf instance providing read-only access to the
// resource contents (in uncompressed form for compressed resources). This
// allows memory-friendly access to large resources by enabling incremental
// processing with transparent decompression for compressed resources.
virtual std::unique_ptr<std::streambuf> streambuf() const = 0;
// Return an std::istream instance providing read-only access to the
// resource contents (in uncompressed form for compressed resources).
//
// The same remark as for streambuf() applies. std::istream is simply a
// higher-level interface than std::streambuf, otherwise both allow the same
// kind of processing.
virtual std::unique_ptr<std::istream> istream() const = 0;
// Return the resource compression type.
virtual CompressionType compressionType() const = 0;
// Return a string description of the resource compression type. Examples:
// "none", "zlib".
virtual std::string compressionDescr() const = 0;
private:
// Pointer to the start of resource contents
const char *_data;
// Size of resource contents, in bytes
std::size_t _size;
};
// Class to describe an uncompressed resource. See AbstractEmbeddedResource.
class RawEmbeddedResource : public AbstractEmbeddedResource
{
public:
explicit RawEmbeddedResource(const char *data, std::size_t size);
AbstractEmbeddedResource::CompressionType compressionType() const override;
std::string compressionDescr() const override;
// The str() method is inherited from AbstractEmbeddedResource
std::unique_ptr<std::streambuf> streambuf() const override;
std::unique_ptr<std::istream> istream() const override;
};
// Class to describe a zlib-compressed resource.
//
// Instances of this class point to resource contents stored in the stream
// format documented in RFC 1950.
class ZlibEmbeddedResource : public AbstractEmbeddedResource
{
public:
explicit ZlibEmbeddedResource(const char *data, std::size_t compressedSize,
std::size_t uncompressedSize);
AbstractEmbeddedResource::CompressionType compressionType() const override;
std::string compressionDescr() const override;
// Return the resource uncompressed size, in bytes.
std::size_t uncompressedSize() const;
std::string str() const override;
std::unique_ptr<std::streambuf> streambuf() const override;
std::unique_ptr<std::istream> istream() const override;
// Getters and setters for parameters used in streambuf() and istream().
// Calling any of the setters affects the subsequent streambuf() and
// istream() calls.
char* getInputBufferStart();
void setInputBufferStart(char* inBuf);
std::size_t getInputBufferSize();
void setInputBufferSize(std::size_t size);
char* getOutputBufferStart();
void setOutputBufferStart(char* outBuf);
std::size_t getOutputBufferSize();
void setOutputBufferSize(std::size_t size);
std::size_t getPutbackSize();
void setPutbackSize(std::size_t size);
private:
std::size_t _uncompressedSize;
char* _inBuf;
std::size_t _inBufSize;
char* _outBuf;
std::size_t _outBufSize;
std::size_t _putbackSize;
};
// These functions are essentially intended for troubleshooting purposes.
std::ostream& operator<<(std::ostream&, const RawEmbeddedResource&);
std::ostream& operator<<(std::ostream&, const ZlibEmbeddedResource&);
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCE_HXX

View File

@@ -1,235 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager.cxx --- Manager class for resources embedded in an
// executable
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <memory>
#include <utility> // std::move()
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <simgear/structure/exception.hxx>
#include "EmbeddedResource.hxx"
#include "EmbeddedResourceManager.hxx"
#include "EmbeddedResourceManager_private.hxx"
using std::string;
using std::shared_ptr;
using std::unique_ptr;
namespace simgear
{
static unique_ptr<EmbeddedResourceManager> staticInstance;
// ***************************************************************************
// * EmbeddedResourceManager::Impl *
// ***************************************************************************
EmbeddedResourceManager::Impl::Impl()
: dirty(true)
{ }
void
EmbeddedResourceManager::Impl::rehash()
{
// Update the list of resource pools to search when looking up a resource.
// This allows to optimize resource lookup: no need to parse, split and hash
// the same locale string every time to find the corresponding resource
// pools.
poolSearchList = listOfResourcePoolsToSearch(selectedLocale);
dirty = false;
}
string
EmbeddedResourceManager::Impl::getLocale() const
{
return selectedLocale;
}
string
EmbeddedResourceManager::Impl::selectLocale(const std::string& locale)
{
string previousLocale = std::move(selectedLocale);
selectedLocale = locale;
dirty = true;
return previousLocale;
}
// Static method
std::vector<string>
EmbeddedResourceManager::Impl::localesSearchList(const string& locale)
{
std::vector<string> result;
if (locale.empty()) {
result.push_back(string()); // only the default locale
} else {
std::size_t sepIdx = locale.find_first_of('_');
if (sepIdx == string::npos) {
// Try the given “locale” first (e.g., fr), then the default locale
result = std::vector<string>({locale, string()});
} else {
string langCode = locale.substr(0, sepIdx);
// Try the given “locale” first (e.g., fr_FR), then the language code
// (e.g., fr) and finally the default locale
result = std::vector<string>({locale, langCode, string()});
}
}
return result;
}
auto
EmbeddedResourceManager::Impl::listOfResourcePoolsToSearch(
const string& locale) const
-> std::vector< shared_ptr<ResourcePool> >
{
std::vector<string> searchedLocales = localesSearchList(locale);
std::vector< shared_ptr<ResourcePool> > result;
for (const string& loc: searchedLocales) {
auto poolPtrIt = localeToResourcePoolMap.find(loc);
// Don't store pointers to empty resource pools in 'result'. This
// optimizes resource fetching a little bit, but requires that all
// resources are added before this method is called.
if (poolPtrIt != localeToResourcePoolMap.end()) {
// Copy a shared_ptr<ResourcePool>
result.push_back(poolPtrIt->second);
}
}
return result;
}
// Static method
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::Impl::lookupResourceInPools(
const string& virtualPath,
const std::vector< shared_ptr<ResourcePool> >& aPoolSearchList)
{
// Search the provided resource pools in proper order. For instance, the one
// for 'fr_FR', then the one for 'fr' and finally the one for the default
// locale. Return the first resource found in one of these pools.
for (const shared_ptr<ResourcePool>& poolPtr: aPoolSearchList) {
auto resourcePtrIt = poolPtr->find(virtualPath);
if (resourcePtrIt != poolPtr->end()) {
// Copy a shared_ptr<const AbstractEmbeddedResource>
return resourcePtrIt->second;
}
}
return shared_ptr<const AbstractEmbeddedResource>(); // null shared_ptr object
}
void
EmbeddedResourceManager::Impl::addResource(
const string& virtualPath,
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const string& locale)
{
// Find the resource pool corresponding to the specified locale
shared_ptr<ResourcePool>& resPoolPtr = localeToResourcePoolMap[locale];
if (!resPoolPtr) {
resPoolPtr.reset(new ResourcePool());
}
auto emplaceRetval = resPoolPtr->emplace(virtualPath, std::move(resourcePtr));
if (!emplaceRetval.second) {
const string localeDescr =
(locale.empty()) ? "the default locale" : "locale '" + locale + "'";
throw sg_error(
"Virtual path already in use for " + localeDescr +
" in the EmbeddedResourceManager: '" + virtualPath + "'");
}
dirty = true;
}
// ***************************************************************************
// * EmbeddedResourceManager *
// ***************************************************************************
EmbeddedResourceManager::EmbeddedResourceManager()
: p(unique_ptr<Impl>(new Impl))
{ }
const unique_ptr<EmbeddedResourceManager>&
EmbeddedResourceManager::createInstance()
{
staticInstance.reset(new EmbeddedResourceManager);
return staticInstance;
}
const unique_ptr<EmbeddedResourceManager>&
EmbeddedResourceManager::instance()
{
return staticInstance;
}
string
EmbeddedResourceManager::getLocale() const
{
return p->getLocale();
}
string
EmbeddedResourceManager::selectLocale(const std::string& locale)
{
return p->selectLocale(locale);
}
void
EmbeddedResourceManager::addResource(
const string& virtualPath,
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const string& locale)
{
p->addResource(virtualPath, std::move(resourcePtr), locale);
}
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath) const
{
if (p->dirty) {
p->rehash(); // update p->poolSearchList
}
// Use the selected locale
return p->lookupResourceInPools(virtualPath, p->poolSearchList);
}
shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath,
const string& locale) const
{
// In this overload, we don't use the cached list of pools
// (p->poolSearchList), therefore there is no need to check the 'dirty' flag
// or to rehash().
return p->lookupResourceInPools(virtualPath,
p->listOfResourcePoolsToSearch(locale));
}
} // of namespace simgear

View File

@@ -1,204 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager.hxx --- Manager class for resources embedded in an
// executable
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCEMANAGER_HXX
#define FG_EMBEDDEDRESOURCEMANAGER_HXX
#include <string>
#include <memory> // std::unique_ptr, std::shared_ptr
#include <unordered_map>
#include <vector>
#include <utility> // std::forward()
#include <cstddef> // std::size_t
#include <simgear/structure/exception.hxx>
#include "EmbeddedResource.hxx"
namespace simgear
{
class EmbeddedResourceManager
{
public:
EmbeddedResourceManager(const EmbeddedResourceManager&) = delete;
EmbeddedResourceManager& operator=(const EmbeddedResourceManager&) = delete;
EmbeddedResourceManager(EmbeddedResourceManager&&) = delete;
EmbeddedResourceManager& operator=(EmbeddedResourceManager&&) = delete;
// The instance is created by createInstance() -> private constructor
// but it should be deleted by its owning std::unique_ptr -> public destructor
~EmbeddedResourceManager() = default;
// Static creator
static const std::unique_ptr<EmbeddedResourceManager>& createInstance();
// Singleton accessor
static const std::unique_ptr<EmbeddedResourceManager>& instance();
// Return the currently-selected “locale”[*] for resource fetching.
//
// [*] For instance: std::string("") for the default locale
// std::string("fr") for French
// std::string("fr_FR") for French from France
std::string getLocale() const;
// Select the locale for which resources will be returned in the future, for
// the getResourceOrNullPtr(), getResource(), getString(), getStreambuf()
// and getIStream() overloads that don't have a 'locale' parameter.
// May be called several times. Return the previously-selected locale.
//
// If you just want to fetch one or two resources in a particular “locale”
// (language), it is simpler to use an overload of one of the
// getResourceOrNullPtr(), getResource(), ..., getIStream() methods that has
// a 'locale' parameter.
std::string selectLocale(const std::string& locale);
// Add a resource for the specified locale to the embedded resource manager.
// This method acts as a sink for its second argument (the std::unique_ptr
// typically has to be std::move()d). If 'locale' is empty, the resource is
// added for the default locale.
void addResource(const std::string& virtualPath,
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const std::string& locale = std::string());
// Get access to a resource.
//
// Fetch the resource for the selected locale (cf. selectLocale()), with
// fallback behavior[1]. If no resource is found for the given
// 'virtualPath', return a null
// std::shared_ptr<const AbstractEmbeddedResource>.
//
// [1] This means that for instance, if the selected locale is 'es_ES', the
// resource is first looked up for the 'es_ES' “locale”; then, if not
// found, for 'es'; and finally, if still not found, for the default
// locale ''.
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
const std::string& virtualPath) const;
// Same as the previous overload, except the resource is fetched for the
// specified locale (with fallback behavior) instead of for the selected
// locale. Use an empty 'locale' parameter to fetch the resource for the
// default locale.
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
const std::string& virtualPath,
const std::string& locale) const;
// Same overloads as for getResourceOrNullPtr(), except that if the resource
// isn't found, then an sg_exception is raised. These methods never return
// a null or empty std::shared_ptr<const AbstractEmbeddedResource>.
template <typename ...Args>
std::shared_ptr<const AbstractEmbeddedResource> getResource(
const std::string& virtualPath, Args&& ...args) const
{
const auto resPtr = getResourceOrNullPtr(virtualPath,
std::forward<Args>(args)...);
if (!resPtr) {
throw sg_exception("No embedded resource found at virtual path '" +
virtualPath + "'");
}
return resPtr;
}
// Get a resource contents in the form of an std::string. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// The returned std::string is a copy of the resource contents (possibly
// transparently decompressed, cf. simgear::ZlibEmbeddedResource).
template <typename ...Args>
std::string getString(const std::string& virtualPath, Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->str();
}
// Get access to a resource via an std::streambuf instance. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// This allows one to incrementally process the resource contents without
// ever making a copy of it (including incremental, transparent
// decompression if the resource happens to be compressed---cf.
// simgear::ZlibEmbeddedResource).
template <typename ...Args>
std::unique_ptr<std::streambuf> getStreambuf(const std::string& virtualPath,
Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->streambuf();
}
// Get access to a resource via an std::istream instance. Raise an
// sg_exception if no resource is found for the specified 'virtualPath'.
//
// The same remarks made for getStreambuf() apply here too.
template <typename ...Args>
std::unique_ptr<std::istream> getIStream(const std::string& virtualPath,
Args&& ...args) const
{
return getResource(virtualPath, std::forward<Args>(args)...)->istream();
}
private:
// Constructor called from createInstance() only
explicit EmbeddedResourceManager();
class Impl;
const std::unique_ptr<Impl> p; // Pimpl idiom
};
// Explicit template instantiations
template
std::shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResource(const std::string& virtualPath) const;
template
std::string
EmbeddedResourceManager::getString(const std::string& virtualPath) const;
template
std::unique_ptr<std::streambuf>
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath) const;
template
std::unique_ptr<std::istream>
EmbeddedResourceManager::getIStream(const std::string& virtualPath) const;
// MSVC doesn't recognize these as template instantiations of what we defined
// above (this seems to be with “Visual Studio 14 2015”), therefore only
// include them with other compilers.
#ifndef _MSC_VER
template
std::shared_ptr<const AbstractEmbeddedResource>
EmbeddedResourceManager::getResource(const std::string& virtualPath,
const std::string& locale) const;
template
std::string
EmbeddedResourceManager::getString(const std::string& virtualPath,
const std::string& locale) const;
template
std::unique_ptr<std::streambuf>
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath,
const std::string& locale) const;
template
std::unique_ptr<std::istream>
EmbeddedResourceManager::getIStream(const std::string& virtualPath,
const std::string& locale) const;
#endif // #ifndef _MSC_VER
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCEMANAGER_HXX

View File

@@ -1,102 +0,0 @@
// -*- coding: utf-8 -*-
//
// EmbeddedResourceManager_private.hxx --- Private implementation class for
// SimGear's EmbeddedResourceManager
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
#define FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
#include <string>
#include <memory> // std::unique_ptr, std::shared_ptr
#include <unordered_map>
#include <vector>
#include "EmbeddedResource.hxx"
namespace simgear
{
class EmbeddedResourceManager::Impl
{
public:
explicit Impl();
// Each “locale” for which addResource() has been used has an associated
// resource pool, that is a sort of directory of all resources declared in
// this locale. The resource pool for a given locale (e.g., 'fr' or 'de_DE')
// maps resource virtual paths to the corresponding resource descriptors
// (via std::shared_ptr<const AbstractEmbeddedResource> instances).
//
// Note: for optimal lookup performance, a tree would probably be better,
// since the expected use for each key here is to store a virtual
// path. But such an optimization is likely unneeded in most cases.
typedef std::unordered_map< std::string,
std::shared_ptr<const AbstractEmbeddedResource> >
ResourcePool;
// Return the list of “locales” to scan to implement fallback behavior when
// fetching a resource for the specified locale. This list will be searched
// from left to right. Examples:
//
// "" -> [""]
// "fr" -> ["fr", ""]
// "fr_FR" -> ["fr_FR", "fr", ""]
static std::vector<std::string> localesSearchList(const std::string& locale);
// Same as localesSearchList(), except it returns the resource pools instead
// of the “locale” strings, and only those pools that are not empty.
std::vector< std::shared_ptr<ResourcePool> > listOfResourcePoolsToSearch(
const std::string& locale) const;
// Look up, in each of the pools referred to by 'poolSearchList', the
// resource associated to 'virtualPath'. Return the first match.
static std::shared_ptr<const AbstractEmbeddedResource> lookupResourceInPools(
const std::string& virtualPath,
const std::vector< std::shared_ptr<ResourcePool> >& poolSearchList);
// Recompute p->poolSearchList. This method is automatically called whenever
// needed (lazily), so it doesn't need to be part of the public interface.
void rehash();
// Implement the corresponding EmbeddedResourceManager public methods
std::string getLocale() const;
std::string selectLocale(const std::string& locale);
// Ditto
void addResource(const std::string& virtualPath,
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
const std::string& locale);
std::string selectedLocale;
// Each call to rehash() updates this member to contain precisely the
// (ordered) list of pools to search for a resource in the selected
// “locale”. This allows relatively cheap resource lookups, assuming the
// desired “locale” doesn't change all the time.
std::vector< std::shared_ptr<ResourcePool> > poolSearchList;
// Indicate whether 'poolSearchList' must be updated (i.e., resources have
// been added or the selected locale was changed without rehash() being
// called afterwards).
bool dirty;
// Maps each “locale name” to the corresponding resource pool.
std::unordered_map< std::string,
std::shared_ptr<ResourcePool> > localeToResourcePoolMap;
};
} // of namespace simgear
#endif // of FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX

View File

@@ -1,412 +0,0 @@
// -*- coding: utf-8 -*-
//
// embedded_resources_test.cxx --- Automated tests for the embedded resources
// system in SimGear
//
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <string>
#include <memory>
#include <ios> // std::streamsize
#include <iostream> // std::cout (used for progress info)
#include <limits> // std::numeric_limits
#include <type_traits> // std::make_unsigned()
#include <sstream>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <simgear/misc/test_macros.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/io/iostreams/CharArrayStream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include "EmbeddedResource.hxx"
#include "EmbeddedResourceManager.hxx"
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::unique_ptr;
using std::shared_ptr;
using simgear::AbstractEmbeddedResource;
using simgear::RawEmbeddedResource;
using simgear::ZlibEmbeddedResource;
using simgear::EmbeddedResourceManager;
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
// Safely convert a non-negative std::streamsize into an std::size_t. If
// impossible, bail out.
std::size_t streamsizeToSize_t(std::streamsize n)
{
SG_CHECK_GE(n, 0);
SG_CHECK_LE(static_cast<uStreamSize>(n),
std::numeric_limits<std::size_t>::max());
return static_cast<std::size_t>(n);
}
// This array is null-terminated, but we'll declare the resource size as
// sizeof(res1Array) - 1 so that the null char is *not* part of it. This
// way allows one to treat text and binary resources exactly the same way,
// with the conversion to std::string via a simple
// std::string(res1Array, resourceSize) not producing a bizarre std::string
// instance whose last character would be '\0' (followed in memory by the same
// '\0' used as C-style string terminator this time!).
static const char res1Array[] = "This is a simple embedded resource test.";
static const char res1frArray[] = "Ceci est un petit test de ressource "
"embarquée.";
static const char res1fr_FRArray[] = "Ceci est un petit test de ressource "
"embarquée (variante fr_FR).";
static const string lipsum = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque congue ornare\n\
congue. Mauris mollis est et porttitor condimentum. Vivamus laoreet blandit\n\
odio eget consectetur. Etiam quis magna eu enim luctus pretium. In et\n\
tristique nunc, non efficitur metus. Nullam efficitur tristique velit.\n\
Praesent et luctus nunc. Mauris eros eros, rutrum at molestie quis, egestas et\n\
lorem. Ut nulla turpis, eleifend sed mauris ac, faucibus molestie nulla.\n\
Quisque viverra vel turpis nec efficitur. Proin non rutrum velit. Nam sodales\n\
metus felis, eu pharetra velit posuere ut.";
// Should be enough to store the compressed lipsum (320 bytes are required
// with zlib 1.2.8, keeping some room to account for possible future format
// changes in the zlib output...). In any case, there is no risk of buffer
// overflow, because simgear::CharArrayOStream prevents this by design.
static char res2Array[350];
static const char res2frArray[] = "Un lorem ipsum un peu plus court...";
// Read data from a string and write it in compressed form to the specified
// buffer.
std::size_t writeCompressedDataToBuffer(const string& inputString,
char *outBuf,
std::size_t outBufSize)
{
simgear::CharArrayOStream res2Writer(outBuf, outBufSize);
std::istringstream iss(inputString);
simgear::ZlibCompressorIStream compressor(
iss,
SGPath(), /* no associated file */
9 /* highest compression level */);
static constexpr std::size_t bufSize = 1024;
unique_ptr<char[]> buf(new char[bufSize]);
std::size_t res2Size = 0;
do {
compressor.read(buf.get(), bufSize);
std::streamsize nBytes = compressor.gcount();
if (nBytes > 0) { // at least one char could be read
res2Writer.write(buf.get(), nBytes);
res2Size += nBytes;
}
} while (compressor && res2Writer);
SG_VERIFY(compressor.eof()); // all the compressed data has been read
// This would fail (among other causes) if the output buffer were too small
// to hold all of the compressed data.
SG_VERIFY(res2Writer);
return res2Size;
}
void initResources()
{
cout << "Creating the EmbeddedResourceManager instance and adding a few "
"resources to it" << endl;
const auto& resMgr = EmbeddedResourceManager::createInstance();
// The resource will *not* consider the null terminator to be in.
unique_ptr<const RawEmbeddedResource> res1(
new RawEmbeddedResource(res1Array, sizeof(res1Array) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1));
unique_ptr<const RawEmbeddedResource> res1fr(
new RawEmbeddedResource(res1frArray, sizeof(res1frArray) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1fr), "fr");
unique_ptr<const RawEmbeddedResource> res1fr_FR(
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray) - 1));
resMgr->addResource("/path/to/resource1", std::move(res1fr_FR), "fr_FR");
// Write the contents of 'lipsum' in compressed form to the 'res2Array'
// static buffer.
std::size_t res2Size = writeCompressedDataToBuffer(lipsum, res2Array,
sizeof(res2Array));
// Now we have a compressed resource to work with, plus the corresponding
// uncompressed output -> perfect for tests!
unique_ptr<const ZlibEmbeddedResource> res2(
new ZlibEmbeddedResource(res2Array, res2Size, lipsum.size()));
resMgr->addResource("/path/to/resource2", std::move(res2));
unique_ptr<const RawEmbeddedResource> res2fr(
new RawEmbeddedResource(res2frArray, sizeof(res2frArray) - 1));
resMgr->addResource("/path/to/resource2", std::move(res2fr), "fr");
// Explicitly select the default locale (typically, English). This is for
// clarity, but isn't required.
resMgr->selectLocale("");
}
// Auxiliary function for test_RawEmbeddedResource()
void auxTest_RawEmbeddedResource_streambuf()
{
cout << "Testing EmbeddedResourceManager::getStreambuf()" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
unique_ptr<std::streambuf> sbuf(resMgr->getStreambuf("/path/to/resource1"));
// Just to show an efficient algorithm. For real applications, use larger
// buffer sizes!
static constexpr std::size_t bufSize = 4;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
std::streamsize nbCharsRead;
string result;
do {
nbCharsRead = sbuf->sgetn(buf.get(), bufSize);
// The conversion to std::size_t is safe because sbuf->sgetn() returned a
// non-negative value which, in this case, can't exceed bufSize.
result.append(buf.get(), streamsizeToSize_t((nbCharsRead)));
} while (nbCharsRead == bufSize);
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
}
// Auxiliary function for test_RawEmbeddedResource()
void auxTest_RawEmbeddedResource_istream()
{
cout << "Testing EmbeddedResourceManager::getIStream()" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
unique_ptr<std::istream> iStream(resMgr->getIStream("/path/to/resource1"));
// This is convenient, but be aware that still in 2017, some buggy C++
// compilers don't allow the exception to be caught: cf.
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145>.
iStream->exceptions(std::ios_base::badbit);
// Just to show an efficient algorithm. For real applications, use larger
// buffer sizes!
static constexpr std::size_t bufSize = 4;
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
string result;
do {
iStream->read(buf.get(), bufSize);
result.append(buf.get(), iStream->gcount());
} while (*iStream); // iStream *points* to an std::istream
// 1) If set, badbit would have caused an exception to be raised (see above).
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
// as the read() call can't provide the requested number of characters.
SG_VERIFY(iStream->eof() && !iStream->bad());
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
}
void test_RawEmbeddedResource()
{
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
"a RawEmbeddedResource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
string("This is a simple embedded resource test."));
// Get a shared_ptr to a const AbstractEmbeddedResource
const auto res1abs = resMgr->getResource("/path/to/resource1");
// Okay because we know this resource is not a compressed one
const auto res1 =
std::dynamic_pointer_cast<const RawEmbeddedResource>(res1abs);
SG_VERIFY(res1);
// Print a representation of the resource metadata
std::cout << "\n/path/to/resource1 -> " << *res1 << "\n\n";
// The following methods would work the same with res1abs
SG_CHECK_EQUAL_NOSTREAM(res1->compressionType(),
AbstractEmbeddedResource::CompressionType::NONE);
SG_CHECK_EQUAL(res1->compressionDescr(), "none");
SG_CHECK_EQUAL(res1->rawPtr(), res1Array);
SG_CHECK_EQUAL(res1->rawSize(), sizeof(res1Array) - 1); // see above
SG_CHECK_EQUAL(res1->str(),
string("This is a simple embedded resource test."));
auxTest_RawEmbeddedResource_streambuf();
auxTest_RawEmbeddedResource_istream();
// Just reload and recheck the resource, because we can :)
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
string("This is a simple embedded resource test."));
}
void test_ZlibEmbeddedResource()
{
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
"a ZlibEmbeddedResource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
lipsum);
// Get a shared_ptr to a const AbstractEmbeddedResource
const auto res2abs = resMgr->getResource("/path/to/resource2");
// Okay because we know this resource is a Zlib-compressed one
const auto res2 =
std::dynamic_pointer_cast<const ZlibEmbeddedResource>(res2abs);
SG_VERIFY(res2);
SG_CHECK_EQUAL(res2->uncompressedSize(), lipsum.size());
// Print a representation of the resource metadata
std::cout << "\n/path/to/resource2 -> " << *res2 << "\n\n";
cout << "Resource 2 compression ratio: " <<
static_cast<float>(res2->uncompressedSize()) /
static_cast<float>(res2->rawSize()) << "\n";
// Just reload and recheck the resource
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"), lipsum);
}
void test_getMissingResources()
{
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
"fetch inexistent resources" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
SG_VERIFY(!resMgr->getResourceOrNullPtr("/inexistant/resource"));
bool gotException = false;
try {
resMgr->getResource("/inexistant/resource");
} catch (const sg_exception&) {
gotException = true;
}
SG_VERIFY(gotException);
gotException = false;
try {
resMgr->getString("/other/inexistant/resource");
} catch (const sg_exception&) {
gotException = true;
}
SG_VERIFY(gotException);
}
void test_addAlreadyExistingResource()
{
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
"add an already existing resource" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
for (const string& locale: {"", "fr", "fr_FR"}) {
// For these tests, we don't care about the resource contents -> no need
// to substract 1 from the result of sizeof() as we did above.
unique_ptr<const RawEmbeddedResource> someRes(
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray)));
bool gotException = false;
try {
resMgr->addResource("/path/to/resource1", std::move(someRes), locale);
} catch (const sg_error&) {
gotException = true;
}
SG_VERIFY(gotException);
}
}
void test_localeDependencyOfResourceFetching()
{
cout << "Testing the locale-dependency of resource fetching from "
"EmbeddedResourceManager" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
resMgr->selectLocale(""); // select the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"This is a simple embedded resource test.");
// Switch to the 'fr_FR' locale (French from France)
resMgr->selectLocale("fr_FR");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"Ceci est un petit test de ressource embarquée (variante "
"fr_FR).");
// This one is for the 'fr' “locale”, obtained as fallback since there is no
// resource mapped to /path/to/resource2 for the 'fr_FR' “locale”.
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
"Un lorem ipsum un peu plus court...");
// Explicitly ask for the resource in the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
"This is a simple embedded resource test.");
// Switch to the 'fr' locale (French)
resMgr->selectLocale("fr");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"Ceci est un petit test de ressource embarquée.");
// Explicitly ask for the resource in the 'fr_FR' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
"Ceci est un petit test de ressource embarquée "
"(variante fr_FR).");
// Switch to the default locale
resMgr->selectLocale("");
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
"This is a simple embedded resource test.");
// Explicitly ask for the resource in the 'fr' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr"),
"Ceci est un petit test de ressource embarquée.");
// Explicitly ask for the resource in the 'fr_FR' locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
"Ceci est un petit test de ressource embarquée "
"(variante fr_FR).");
// Explicitly ask for the resource in the default locale
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
"This is a simple embedded resource test.");
}
void test_getLocaleAndSelectLocale()
{
cout << "Testing the getLocale() and selectLocale() methods of "
"EmbeddedResourceManager" << endl;
const auto& resMgr = EmbeddedResourceManager::instance();
for (const string& locale: {"", "fr", "fr_FR", "de_DE"}) {
// The important effects of setLocale() are tested in
// test_localeDependencyOfResourceFetching()
resMgr->selectLocale(locale);
SG_CHECK_EQUAL(resMgr->getLocale(), locale);
}
}
int main(int argc, char **argv)
{
// Initialize the EmbeddedResourceManager instance, add a few resources
// to it and call its selectLocale() method.
initResources();
test_RawEmbeddedResource();
test_ZlibEmbeddedResource();
test_getMissingResources();
test_addAlreadyExistingResource();
test_localeDependencyOfResourceFetching();
test_getLocaleAndSelectLocale();
return EXIT_SUCCESS;
}

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"
@@ -169,32 +168,8 @@ void Client::update(int waitTimeout)
return;
}
int remainingActive, messagesInQueue;
#if defined(SG_MAC)
// Mac 10.8 libCurl lacks this, let's keep compat for now
fd_set curlReadFDs, curlWriteFDs, curlErrorFDs;
int maxFD;
curl_multi_fdset(d->curlMulti,
&curlReadFDs,
&curlWriteFDs,
&curlErrorFDs,
&maxFD);
struct timeval timeout;
long t;
curl_multi_timeout(d->curlMulti, &t);
if ((t < 0) || (t > waitTimeout)) {
t = waitTimeout;
}
timeout.tv_sec = t / 1000;
timeout.tv_usec = (t % 1000) * 1000;
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout);
#else
int numFds;
int remainingActive, messagesInQueue, numFds;
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
#endif
curl_multi_perform(d->curlMulti, &remainingActive);
CURLMsg* msg;

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,16 +4,12 @@ set(HEADERS
sgstream.hxx
gzfstream.hxx
gzcontainerfile.hxx
CharArrayStream.hxx
zlibstream.hxx
)
set(SOURCES
sgstream.cxx
gzfstream.cxx
gzcontainerfile.cxx
CharArrayStream.cxx
zlibstream.cxx
)
simgear_component(IOStreams io/iostreams "${SOURCES}" "${HEADERS}")
@@ -24,12 +20,4 @@ if(ENABLE_TESTS)
target_link_libraries(test_streams ${TEST_LIBS})
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
add_executable(test_CharArrayStream CharArrayStream_test.cxx)
target_link_libraries(test_CharArrayStream ${TEST_LIBS})
add_test(CharArrayStream ${EXECUTABLE_OUTPUT_PATH}/test_CharArrayStream)
add_executable(test_zlibstream zlibstream_test.cxx)
target_link_libraries(test_zlibstream ${TEST_LIBS})
add_test(zlibstream ${EXECUTABLE_OUTPUT_PATH}/test_zlibstream)
endif(ENABLE_TESTS)

View File

@@ -1,291 +0,0 @@
// -*- coding: utf-8 -*-
//
// CharArrayStream.cxx --- IOStreams classes for reading from, and writing to
// char arrays
//
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <ios> // std::streamsize
#include <istream> // std::istream and std::iostream
#include <ostream> // std::ostream
#include <type_traits> // std::make_unsigned()
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <cassert>
#include "CharArrayStream.hxx"
using traits = std::char_traits<char>;
namespace simgear
{
// ***************************************************************************
// * CharArrayStreambuf class *
// ***************************************************************************
CharArrayStreambuf::CharArrayStreambuf(char* buf, std::size_t bufSize)
: _buf(buf),
_bufSize(bufSize)
{
setg(_buf, _buf, _buf + _bufSize);
setp(_buf, _buf + _bufSize);
}
char* CharArrayStreambuf::data() const
{
return _buf;
}
std::size_t CharArrayStreambuf::size() const
{
return _bufSize;
}
int CharArrayStreambuf::underflow()
{
return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr());
}
int CharArrayStreambuf::overflow(int c)
{
// cf. §27.7.1, footnote 309 of the C++11 standard
if (traits::eq_int_type(c, traits::eof())) {
return traits::not_eof(c);
} else {
// This class never writes beyond the end of the array (_buf + _bufSize)
return traits::eof();
}
}
std::streamsize CharArrayStreambuf::xsgetn(char* dest, std::streamsize n)
{
assert(n >= 0);
std::ptrdiff_t avail = egptr() - gptr();
// Compute min(avail, n). The cast is safe, because in its branch, one has
// 0 <= n < avail, which is of type std::ptrdiff_t.
std::ptrdiff_t nbChars = ( (n >= avail) ?
avail : static_cast<std::ptrdiff_t>(n) );
std::copy(gptr(), gptr() + nbChars, dest);
// eback() == _buf and egptr() == _buf + _bufSize
// I don't use gbump(), because it takes an int...
setg(eback(), gptr() + nbChars, egptr());
// Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
return static_cast<std::streamsize>(nbChars); // number of chars copied
}
std::streamsize CharArrayStreambuf::xsputn(const char* s, std::streamsize n)
{
assert(n >= 0);
std::ptrdiff_t availSpace = epptr() - pptr();
// Compute min(availSpace, n). The cast is safe, because in its branch, one
// has 0 <= n < availSpace, which is of type std::ptrdiff_t.
std::ptrdiff_t nbChars = ( (n >= availSpace) ?
availSpace : static_cast<std::ptrdiff_t>(n) );
std::copy(s, s + nbChars, pptr());
// epptr() == _buf + _bufSize
// I don't use pbump(), because it takes an int...
setp(pptr() + nbChars, epptr());
// Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
return static_cast<std::streamsize>(nbChars); // number of chars copied
}
std::streamsize CharArrayStreambuf::showmanyc()
{
// It is certain that underflow() will return EOF if gptr() == egptr().
return -1;
}
std::streampos CharArrayStreambuf::seekoff(std::streamoff off,
std::ios_base::seekdir way,
std::ios_base::openmode which)
{
bool positionInputSeq = false;
bool positionOutputSeq = false;
char* ptr = nullptr;
// cf. §27.8.2.4 of the C++11 standard
if ((which & std::ios_base::in) == std::ios_base::in) {
positionInputSeq = true;
ptr = gptr();
}
if ((which & std::ios_base::out) == std::ios_base::out) {
positionOutputSeq = true;
ptr = pptr();
}
if ((!positionInputSeq && !positionOutputSeq) ||
(positionInputSeq && positionOutputSeq &&
way != std::ios_base::beg && way != std::ios_base::end)) {
return std::streampos(std::streamoff(-1));
}
// If we reached this point and (positionInputSeq && positionOutputSeq),
// then (way == std::ios_base::beg || way == std::ios_base::end) and
// therefore 'ptr' won't be used.
std::streamoff refOffset;
static_assert(sizeof(std::streamoff) >= sizeof(std::ptrdiff_t),
"Unexpected: sizeof(std::streamoff) < sizeof(std::ptrdiff_t)");
static_assert(sizeof(std::streamoff) >= sizeof(std::size_t),
"Unexpected: sizeof(std::streamoff) < sizeof(std::size_t)");
if (way == std::ios_base::beg) {
refOffset = 0;
} else if (way == std::ios_base::cur) {
refOffset = static_cast<std::streamoff>(ptr - _buf);
} else {
assert(way == std::ios_base::end);
refOffset = static_cast<std::streamoff>(_bufSize);
}
// Offset, relatively to _buf, where we are supposed to seek
std::streamoff totalOffset = refOffset + off;
typedef typename std::make_unsigned<std::streamoff>::type uStreamOff;
if (totalOffset < 0 || static_cast<uStreamOff>(totalOffset) > _bufSize) {
return std::streampos(std::streamoff(-1));
} else {
// Safe because 0 <= totalOffset <= _bufSize, which is an std::size_t
char* newPtr = _buf + static_cast<std::size_t>(totalOffset);
if (positionInputSeq) {
// eback() == _buf and egptr() == _buf + _bufSize
setg(eback(), newPtr, egptr());
}
if (positionOutputSeq) {
// epptr() == _buf + _bufSize
setp(newPtr, epptr());
}
// C++11's §27.8.2.4 item 12 (for stringbuf) would return refOffset. This
// makes no sense IMHO, in particular when 'way' is std::ios_base::beg or
// std::ios_base::end. Return the new offset (from the beginning of
// '_buf') instead. Note that this doesn't violate anything, because
// §27.6.3.4.2 grants full freedom as to the semantics of seekoff() to
// classes derived from basic_streambuf.
//
// My interpretation is consistent with items 13 and 14 of §27.8.2.4
// concerning seekpos(), whereas item 12 is not (if item 12 were followed
// to the letter, seekoff() would always return 0 on success when
// way == std::ios_base::beg, and therefore items 13 and 14 would be
// incompatible).
return std::streampos(totalOffset);
}
}
std::streampos CharArrayStreambuf::seekpos(std::streampos pos,
std::ios_base::openmode which)
{
return seekoff(std::streamoff(pos), std::ios_base::beg, which);
}
// ***************************************************************************
// * ROCharArrayStreambuf class *
// ***************************************************************************
ROCharArrayStreambuf::ROCharArrayStreambuf(const char* buf, std::size_t bufSize)
: CharArrayStreambuf(const_cast<char*>(buf), bufSize)
{ }
const char* ROCharArrayStreambuf::data() const
{
return const_cast<const char*>(CharArrayStreambuf::data());
}
int ROCharArrayStreambuf::overflow(int c)
{
return traits::eof(); // indicate failure
}
std::streamsize ROCharArrayStreambuf::xsputn(const char* s, std::streamsize n)
{
return 0; // number of chars written
}
// ***************************************************************************
// * CharArrayIStream class *
// ***************************************************************************
CharArrayIStream::CharArrayIStream(const char* buf, std::size_t bufSize)
: std::istream(nullptr),
_streamBuf(buf, bufSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
}
const char* CharArrayIStream::data() const
{
return _streamBuf.data();
}
std::size_t CharArrayIStream::size() const
{
return _streamBuf.size();
}
// ***************************************************************************
// * CharArrayOStream class *
// ***************************************************************************
CharArrayOStream::CharArrayOStream(char* buf, std::size_t bufSize)
: std::ostream(nullptr),
_streamBuf(buf, bufSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
}
char* CharArrayOStream::data() const
{
return _streamBuf.data();
}
std::size_t CharArrayOStream::size() const
{
return _streamBuf.size();
}
// ***************************************************************************
// * CharArrayIOStream class *
// ***************************************************************************
CharArrayIOStream::CharArrayIOStream(char* buf, std::size_t bufSize)
: std::iostream(nullptr),
_streamBuf(buf, bufSize)
{
// Associate _streamBuf to 'this' and clear the error state flags
rdbuf(&_streamBuf);
}
char* CharArrayIOStream::data() const
{
return _streamBuf.data();
}
std::size_t CharArrayIOStream::size() const
{
return _streamBuf.size();
}
} // of namespace simgear

View File

@@ -1,169 +0,0 @@
// -*- coding: utf-8 -*-
//
// CharArrayStream.hxx --- IOStreams classes for reading from, and writing to
// char arrays
//
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#ifndef SG_CHAR_ARRAY_STREAM_HXX
#define SG_CHAR_ARRAY_STREAM_HXX
#include <istream>
#include <ostream>
#include <streambuf>
#include <ios> // std::streamsize, std::streampos...
#include <cstddef> // std::size_t
// Contrary to std::stringstream and its (i/o)stringstream friends, the
// classes in this file allow one to work on an array of char (that could be
// for instance static data) without having to make a whole copy of it.
//
// There are five classes defined here in the 'simgear' namespace:
// - CharArrayStreambuf subclass of std::streambuf stream buffer
// - ROCharArrayStreambuf subclass of CharArrayStreambuf stream buffer
// - CharArrayIStream subclass of std::istream input stream
// - CharArrayOStream subclass of std::ostream output stream
// - CharArrayIOStream subclass of std::iostream input/output stream
//
// The main class is CharArrayStreambuf. ROCharArrayStreambuf is a read-only
// subclass of CharArrayStreambuf. The other three are very simple convenience
// classes, using either CharArrayStreambuf or ROCharArrayStreambuf as their
// stream buffer class. One can easily work with CharArrayStreambuf or
// ROCharArrayStreambuf only, either directly or after attaching an instance
// to an std::istream, std::ostream or std::iostream instance (using for
// example constructors like std::istream(std::streambuf* sb) or the rdbuf()
// method of stream classes).
namespace simgear
{
// Input/output stream buffer class that reads from, and writes to a fixed
// buffer in memory specified in the constructor. This buffer must remain
// alive as long as the stream buffer object is used (the CharArrayStreambuf
// class works directly on that buffer without making any copy of it).
//
// Because reads and writes are directly performed on the buffer specified in
// the constructor, this stream buffer class has no caching behavior. You may
// use pubsync() if you like, but that is completely useless by design (it
// uses the default implementation in std::streambuf, which does nothing).
//
// CharArrayStreambuf may share similarities in features with
// std::strstreambuf (deprecated since C++98). However, at least one big
// difference is that CharArrayStreambuf does no dynamic memory allocation
// whatsoever. It works on a fixed-size-fixed-location buffer passed in the
// constructor, and nothing more. It does prevent overflowing the buffer,
// since it knows perfectly well where the buffer starts and ends.
class CharArrayStreambuf: public std::streambuf
{
public:
explicit CharArrayStreambuf(char* buf, std::size_t bufSize);
// Accessors for the buffer start pointer and size (same method names as for
// std::string)
char* data() const;
std::size_t size() const;
protected:
virtual int underflow() override;
virtual int overflow(int c = std::char_traits<char>::eof()) override;
// Optional override when subclassing std::streambuf. This is the most
// efficient way of reading several characters.
virtual std::streamsize xsgetn(char* dest, std::streamsize n) override;
// Ditto for writing
virtual std::streamsize xsputn(const char* s, std::streamsize n) override;
virtual std::streamsize showmanyc() override;
virtual std::streampos seekoff(
std::streamoff off,
std::ios_base::seekdir way,
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
override;
virtual std::streampos seekpos(
std::streampos pos,
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
override;
private:
// These two define the buffer managed by the CharArrayStreambuf instance.
char* const _buf;
const std::size_t _bufSize;
};
// Read-only version of CharArrayStreambuf
class ROCharArrayStreambuf: public CharArrayStreambuf
{
public:
explicit ROCharArrayStreambuf(const char* buf, std::size_t bufSize);
// Accessor for the buffer start pointer (same method name as for
// std::string)
const char* data() const;
private:
// Override methods pertaining to write access
virtual int overflow(int c = std::char_traits<char>::eof()) override;
virtual std::streamsize xsputn(const char* s, std::streamsize n) override;
};
// Convenience class: std::istream subclass based on ROCharArrayStreambuf
class CharArrayIStream: public std::istream
{
public:
// Same parameters as for ROCharArrayStreambuf
explicit CharArrayIStream(const char* buf, std::size_t bufSize);
// Accessors for the underlying buffer start pointer and size
const char* data() const;
std::size_t size() const;
private:
ROCharArrayStreambuf _streamBuf;
};
// Convenience class: std::ostream subclass based on CharArrayStreambuf
class CharArrayOStream: public std::ostream
{
public:
// Same parameters as for CharArrayStreambuf
explicit CharArrayOStream(char* buf, std::size_t bufSize);
// Accessors for the underlying buffer start pointer and size
char* data() const;
std::size_t size() const;
private:
CharArrayStreambuf _streamBuf;
};
// Convenience class: std::iostream subclass based on CharArrayStreambuf
class CharArrayIOStream: public std::iostream
{
public:
// Same parameters as for CharArrayStreambuf
explicit CharArrayIOStream(char* buf, std::size_t bufSize);
// Accessors for the underlying buffer start pointer and size
char* data() const;
std::size_t size() const;
private:
CharArrayStreambuf _streamBuf;
};
} // of namespace simgear
#endif // of SG_CHAR_ARRAY_STREAM_HXX

View File

@@ -1,439 +0,0 @@
// -*- coding: utf-8 -*-
//
// CharArrayStream_test.cxx --- Automated tests for CharArrayStream.cxx
//
// Copyright (C) 2017 Florent Rougon
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA.
#include <simgear_config.h>
#include <ios> // std::basic_ios, std::streamsize...
#include <iostream> // std::ios_base, std::cerr, etc.
#include <sstream>
#include <limits> // std::numeric_limits
#include <type_traits> // std::make_unsigned()
#include <memory> // std::unique_ptr
#include <cassert>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <cstring> // std::strlen()
#include <algorithm> // std::fill_n()
#include <vector>
#include <simgear/misc/test_macros.hxx>
#include "CharArrayStream.hxx"
using std::string;
using std::cout;
using std::cerr;
using traits = std::char_traits<char>;
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
// Safely convert a non-negative std::streamsize into an std::size_t. If
// impossible, bail out.
static std::size_t streamsizeToSize_t(std::streamsize n)
{
SG_CHECK_GE(n, 0);
SG_CHECK_LE(static_cast<uStreamSize>(n),
std::numeric_limits<std::size_t>::max());
return static_cast<std::size_t>(n);
}
void test_CharArrayStreambuf_basicOperations()
{
cerr << "Testing basic operations on CharArrayStreambuf\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
std::istringstream text_ss(text);
string canary = "YoY";
// Reserve space for our little canary
const std::size_t bufSize = text.size() + canary.size();
std::unique_ptr<char[]> buf(new char[bufSize]);
int ch;
std::streamsize n;
std::streampos pos;
// Only allow arraySBuf to read from, and write to the first text.size()
// chars of 'buf'
simgear::CharArrayStreambuf arraySBuf(&buf[0], text.size());
// 1) Write a copy of the 'text' string at buf.get(), testing various write
// and seek methods.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Write "01" with two sputc() calls
ch = arraySBuf.sputc(text[0]);
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0' && buf[0] == '0');
ch = arraySBuf.sputc(text[1]);
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1' && buf[1] == '1');
// Write the 34 following chars of 'text' with one sputn() call
n = arraySBuf.sputn(&text[2], 34);
SG_CHECK_EQUAL(n, 34);
SG_CHECK_EQUAL(string(&buf[2], 34), "23456789abcdefghijklmnopqrstuvwxyz");
// Indirect test of seekpos(): position the write stream pointer a bit further
pos = arraySBuf.pubseekpos(43, std::ios_base::out);
SG_CHECK_EQUAL(pos, std::streampos(43));
// Write 7 more chars with sputn()
n = arraySBuf.sputn(&text[43], 7);
SG_CHECK_EQUAL(n, 7);
SG_CHECK_EQUAL(string(&buf[43], 7), "\nGHIJK ");
// Indirect test of seekoff(): seek backwards relatively to the current write
// pointer position
pos = arraySBuf.pubseekoff(-std::streamoff(std::strlen("\nABCDEF\nGHIJK ")),
std::ios_base::cur, std::ios_base::out);
// 10 + 26, i.e., after the lowercase alphabet
SG_CHECK_EQUAL(pos, std::streampos(36));
// Write "\nABCD" to buf in one sputn() call
n = arraySBuf.sputn(&text[36], 5);
// Now write "EF" in two sputc() calls
ch = arraySBuf.sputc(text[41]);
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'E' && buf[41] == 'E');
ch = arraySBuf.sputc(text[42]);
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'F' && buf[42] == 'F');
// Place a canary to check that arraySBuf doesn't write beyond the end of buf
std::copy(canary.begin(), canary.end(), &buf[text.size()]);
// Check seeking from arraySBuf's end (which is at offset text.size(), *not*
// bufSize: cf. the construction of arraySBuf!).
pos = arraySBuf.pubseekoff(-std::streamoff(std::strlen("LMNOPQ")),
std::ios_base::end, std::ios_base::out);
SG_CHECK_EQUAL(pos, std::streampos(text.size() - std::strlen("LMNOPQ")));
// Write "LMNOPQ" to buf in one sputn() call. The other characters won't be
// written, because they would go past the end of the buffer managed by
// 'arraySBuf' (i.e., the first text.size() chars of 'buf').
static const char someChars[] = "LMNOPQ+buffer overrun that will be blocked";
n = arraySBuf.sputn(someChars, sizeof(someChars));
// Check the number of chars actually written
SG_CHECK_EQUAL(n, std::strlen("LMNOPQ"));
// Check that our canary starting at buf[text.size()] is still there and
// intact
SG_CHECK_EQUAL(string(&buf[text.size()], canary.size()), canary);
// Check that we now have an exact copy of 'text' in the managed buffer
SG_CHECK_EQUAL(string(&buf[0], text.size()), text);
// 2) Read back the copy of 'text' in 'buf', using various read and seek
// methods.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ch = arraySBuf.sgetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
ch = arraySBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
ch = arraySBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1');
ch = arraySBuf.snextc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
ch = arraySBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
ch = arraySBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
ch = arraySBuf.sputbackc('4');
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
ch = arraySBuf.sputbackc('u'); // doesn't match what we read from the stream
SG_VERIFY(ch == EOF);
ch = arraySBuf.sputbackc('3'); // this one does
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
static constexpr std::streamsize buf2Size = 10;
char buf2[buf2Size];
// Most efficient way (with the underlying xsgetn()) to read several chars
// at once.
n = arraySBuf.sgetn(buf2, buf2Size);
SG_CHECK_EQUAL(n, buf2Size);
SG_CHECK_EQUAL(string(buf2, static_cast<std::size_t>(buf2Size)),
"3456789abc");
ch = arraySBuf.sungetc(); // same as sputbackc(), except no value to check
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
ch = arraySBuf.sungetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
ch = arraySBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
ch = arraySBuf.sputbackc('b'); // this one does
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
n = arraySBuf.sgetn(buf2, buf2Size);
SG_CHECK_EQUAL(n, buf2Size);
SG_CHECK_EQUAL(string(buf2, static_cast<std::size_t>(buf2Size)),
"bcdefghijk");
ch = arraySBuf.sungetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
static char buf3[64];
n = arraySBuf.sgetn(buf3, sizeof(buf3));
SG_CHECK_EQUAL(n, 36);
SG_CHECK_EQUAL(string(buf3, 36), "klmnopqrstuvwxyz\nABCDEF\nGHIJK LMNOPQ");
SG_CHECK_EQUAL(arraySBuf.sbumpc(), EOF);
// Check we can independently set the read and write pointers for arraySBuf
pos = arraySBuf.pubseekpos(10, std::ios_base::in);
SG_CHECK_EQUAL(pos, std::streampos(10));
pos = arraySBuf.pubseekpos(13, std::ios_base::out);
SG_CHECK_EQUAL(pos, std::streampos(13));
// Write "DEF" where there is currently "def" in 'buf'.
for (int i = 0; i < 3; i++) {
char c = 'D' + i;
ch = arraySBuf.sputc(c);
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == c && buf[i+13] == c);
}
n = arraySBuf.sgetn(buf3, 6);
SG_CHECK_EQUAL(n, 6);
SG_CHECK_EQUAL(string(buf3, 6), "abcDEF");
// Set both stream pointers at once (read and write)
pos = arraySBuf.pubseekpos(10, std::ios_base::in | std::ios_base::out);
SG_VERIFY(pos == std::streampos(10));
// Write "ABC" to buf in one sputn() call
n = arraySBuf.sputn("ABC", 3);
SG_CHECK_EQUAL(n, 3);
SG_CHECK_EQUAL(string(&buf[10], 3), "ABC");
// Indirect test of seekoff(): seek backwards relatively to the current read
// pointer position
pos = arraySBuf.pubseekoff(-3, std::ios_base::cur, std::ios_base::in);
SG_CHECK_EQUAL(pos, std::streampos(7));
n = arraySBuf.sgetn(buf3, 12);
SG_CHECK_EQUAL(n, 12);
SG_CHECK_EQUAL(string(buf3, 12), "789ABCDEFghi");
}
void test_CharArrayStreambuf_readOrWriteLargestPossibleAmount()
{
cerr << "Testing reading and writing from/to CharArrayStreambuf with the "
"largest possible value passed as the 'n' argument for sgetn()/sputn() "
"(number of chars to read or write)\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
string canary = "ZaZ";
// Reserve space for our little canary
const std::size_t bufSize = text.size() + canary.size();
std::unique_ptr<char[]> buf(new char[bufSize]);
std::streamsize n_s;
std::size_t n;
std::streampos pos;
// Place a canary to check that arraySBuf doesn't write beyond the end of buf
std::copy(canary.begin(), canary.end(), &buf[text.size()]);
// Only allow arraySBuf to read from, and write to the first text.size()
// chars of 'buf'
simgear::CharArrayStreambuf arraySBuf(&buf[0], text.size());
n_s = arraySBuf.sputn(text.c_str(),
std::numeric_limits<std::streamsize>::max());
// The conversion to std::size_t is safe because arraySBuf.sputn() returns a
// non-negative value which, in this case, can't exceed the size of the
// buffer managed by 'arraySBuf', i.e. text.size().
n = streamsizeToSize_t(n_s);
SG_CHECK_EQUAL(n, arraySBuf.size());
SG_CHECK_EQUAL(n, text.size());
SG_CHECK_EQUAL(string(&buf[0], n), text);
// Check that our canary starting at &buf[text.size()] is still there and
// intact
SG_CHECK_EQUAL(string(&buf[text.size()], canary.size()), canary);
// The “get” stream pointer is still at the beginning of the buffer managed
// by 'arraySBuf'. Let's ask for the maximum amount of chars from it to be
// written to a new buffer, 'buf2'.
std::unique_ptr<char[]> buf2(new char[text.size()]);
n_s = arraySBuf.sgetn(&buf2[0],
std::numeric_limits<std::streamsize>::max());
// The conversion to std::size_t is safe because arraySBuf.sgetn() returns a
// non-negative value which, in this case, can't exceed the size of the
// buffer managed by 'arraySBuf', i.e. text.size().
n = streamsizeToSize_t(n_s);
SG_CHECK_EQUAL(n, arraySBuf.size());
SG_CHECK_EQUAL(string(&buf2[0], n), text);
SG_CHECK_EQUAL(arraySBuf.sbumpc(), EOF);
}
void test_CharArrayIStream_simple()
{
// This also tests ROCharArrayStreambuf, since it is used as
// CharArrayIStream's stream buffer class.
cerr << "Testing read operations from CharArrayIStream\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
std::unique_ptr<char[]> buf(new char[text.size()]);
std::size_t n;
simgear::CharArrayIStream caStream(&text[0], text.size());
caStream.exceptions(std::ios_base::badbit); // throw if badbit is set
SG_CHECK_EQUAL(caStream.data(), &text[0]);
SG_CHECK_EQUAL(caStream.size(), text.size());
SG_VERIFY(caStream.get(buf[0])); // get pointer = 1
SG_CHECK_EQUAL(buf[0], text[0]);
caStream.putback(buf[0]); // get pointer = 0
SG_CHECK_EQUAL(caStream.get(), traits::to_int_type(text[0])); // get ptr = 1
// std::iostream::operator bool() will return false due to EOF being reached
SG_VERIFY(!caStream.read(&buf[1],
std::numeric_limits<std::streamsize>::max()));
// If badbit had been set, it would have caused an exception to be raised
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
// The conversion to std::size_t is safe because caStream.gcount() returns a
// non-negative value which, in this case, can't exceed the size of the
// buffer managed by caStream's associated stream buffer, i.e. text.size().
n = streamsizeToSize_t(caStream.gcount());
SG_CHECK_EQUAL(n, caStream.size() - 1);
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
SG_CHECK_EQUAL(caStream.get(), EOF);
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
// Test stream extraction: operator>>()
caStream.clear(); // clear the error state flags
SG_VERIFY(caStream.seekg(0)); // rewind
std::vector<string> expectedWords = {
"0123456789abcdefghijklmnopqrstuvwxyz",
"ABCDEF",
"GHIJK",
"LMNOPQ"
};
string str;
for (int i = 0; caStream >> str; i++) {
SG_CHECK_EQUAL(str, expectedWords[i]);
}
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
}
void test_CharArrayOStream_simple()
{
cerr << "Testing write operations to CharArrayOStream\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
// One could also use an std::vector<char>, but then beware of reallocations!
std::unique_ptr<char[]> buf(new char[text.size()]);
std::fill_n(buf.get(), text.size(), '\0'); // to ensure reproducible results
simgear::CharArrayOStream caStream(&buf[0], text.size());
SG_CHECK_EQUAL(caStream.data(), &buf[0]);
SG_CHECK_EQUAL(caStream.size(), text.size());
SG_VERIFY(caStream.put(text[0])); // buf[0] = text[0], put pointer = 1
SG_CHECK_EQUAL(buf[0], text[0]);
SG_VERIFY(caStream.seekp(8)); // put pointer = 8
// buf[8:23] = text[8:23] (meaning: buf[8] = text[8], ..., buf[22] = text[22])
SG_VERIFY(caStream.write(&text[8], 15)); // put pointer = 23
buf[1] = 'X'; // write garbage to buf[1]
buf[2] = 'Y'; // and to buf[2]
SG_VERIFY(caStream.seekp(-22, std::ios_base::cur)); // put pointer = 23-22 = 1
SG_VERIFY(caStream.write(&text[1], 7)); // buf[1:8] = text[1:8]
// The std::ios_base::beg argument is superfluous here---just for testing.
SG_VERIFY(caStream.seekp(23, std::ios_base::beg)); // put pointer = 23
// Test stream insertion: operator<<()
SG_VERIFY(caStream << text.substr(23, 10));
SG_VERIFY(caStream.write(&text[33], text.size() - 33)); // all that remains
SG_VERIFY(!caStream.put('Z')); // doesn't fit in caStream's buffer
SG_VERIFY(caStream.bad()); // put() set the stream's badbit flag
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
}
void test_CharArrayIOStream_readWriteSeekPutbackEtc()
{
cerr << "Testing read, write, seek, putback... from/to CharArrayIOStream\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
std::unique_ptr<char[]> buf(new char[text.size()]);
std::size_t n;
char ch;
simgear::CharArrayIOStream caStream(&buf[0], text.size());
caStream.exceptions(std::ios_base::badbit); // throw if badbit is set
SG_CHECK_EQUAL(caStream.data(), &buf[0]);
SG_CHECK_EQUAL(caStream.size(), text.size());
SG_VERIFY(caStream.put(text[0])); // buf[0] = text[0], put pointer = 1
SG_CHECK_EQUAL(buf[0], text[0]);
SG_VERIFY(caStream.get(ch)); // read it back from buf, get pointer = 1
SG_CHECK_EQUAL(ch, text[0]);
caStream.putback(buf[0]); // get pointer = 0
SG_CHECK_EQUAL(caStream.get(), traits::to_int_type(text[0])); // get ptr = 1
SG_VERIFY(caStream.seekp(5));
// buf[5:10] = text[5:10] (meaning: buf[5] = text[5], ..., buf[9] = text[9])
SG_VERIFY(caStream.write(&text[5], 5)); // put pointer = 10
buf[1] = 'X'; // write garbage to buf[1]
buf[2] = 'Y'; // and to buf[2]
SG_VERIFY(caStream.seekp(-9, std::ios_base::cur)); // put pointer = 10 - 9 = 1
SG_VERIFY(caStream.write(&text[1], 4)); // buf[1:5] = text[1:5]
SG_VERIFY(caStream.seekp(10)); // put pointer = 10
// buf[10:] = text[10:]
SG_VERIFY(caStream.write(&text[10], text.size() - 10));
std::unique_ptr<char[]> buf2(new char[caStream.size() - 10]);
SG_VERIFY(caStream.seekg(10)); // get pointer = 10
// std::iostream::operator bool() will return false due to EOF being reached
SG_VERIFY(!caStream.read(&buf2[0],
std::numeric_limits<std::streamsize>::max()));
// If badbit had been set, it would have caused an exception to be raised
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
// The conversion to std::size_t is safe because caStream.gcount() returns a
// non-negative value which, in this case, can't exceed the size of the
// buffer managed by caStream's associated stream buffer, i.e. text.size().
n = streamsizeToSize_t(caStream.gcount());
SG_CHECK_EQUAL(n, caStream.size() - 10);
SG_CHECK_EQUAL(caStream.get(), EOF);
SG_CHECK_EQUAL(string(&buf2[0], caStream.size() - 10),
string(&text[10], text.size() - 10));
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
}
int main(int argc, char** argv)
{
test_CharArrayStreambuf_basicOperations();
test_CharArrayStreambuf_readOrWriteLargestPossibleAmount();
test_CharArrayIStream_simple();
test_CharArrayOStream_simple();
test_CharArrayIOStream_readWriteSeekPutbackEtc();
return EXIT_SUCCESS;
}

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 );
@@ -1054,23 +1055,3 @@ bool SGPath::permissionsAllowsWrite() const
return _permission_checker ? _permission_checker(*this).write : true;
}
//------------------------------------------------------------------------------
std::string SGPath::fileUrl() const
{
// we should really URL encode the names here?
if (isAbsolute()) {
// check for a windows drive letter
#if defined(SG_WINDOWS)
if (isalpha(path.front())) {
// file URLs on Windows must look like file:///C:/Foo/Bar
return "file:///" + utf8Str();
}
#endif
// the leading directory seperator of the path becomes the required
// third slash in this case.
return "file://" + utf8Str();
} else {
SG_LOG(SG_GENERAL, SG_WARN, "Cannot convert relative path to a URL:" << path);
return {};
}
}

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
{
@@ -281,11 +281,6 @@ public:
*/
SGPath dirPath() const;
/*
* return path as a file:// URI
*/
std::string fileUrl() const;
enum StandardLocation
{
HOME,

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

@@ -14,7 +14,6 @@ set(SOURCES
Package.cxx
Install.cxx
Root.cxx
Delegate.cxx
# internal helpers
md5.h md5.c
ioapi.c ioapi_mem.c ioapi.h

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

@@ -1,39 +0,0 @@
// Copyright (C) 2017 James Turner - zakalawe@mac.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear/package/Delegate.hxx>
#include <simgear/package/Install.hxx>
#include <simgear/package/Package.hxx>
#include <simgear/package/Catalog.hxx>
namespace simgear
{
namespace pkg
{
void Delegate::installStatusChanged(InstallRef aInstall, StatusCode aReason)
{
}
void Delegate::dataForThumbnail(const std::string& aThumbnailUrl,
size_t lenth, const uint8_t* bytes)
{
}
} // of namespace pkg
} // of namespace simgear

View File

@@ -18,7 +18,6 @@
#ifndef SG_PACKAGE_DELEGATE_HXX
#define SG_PACKAGE_DELEGATE_HXX
#include <string>
#include <simgear/misc/stdint.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
@@ -53,7 +52,6 @@ public:
FAIL_FILESYSTEM, ///< unknown filesystem error occurred
FAIL_VERSION, ///< version check mismatch
FAIL_NOT_FOUND, ///< package URL returned a 404
FAIL_HTTP_FORBIDDEN, ///< URL returned a 403. Marked specially to catch rate-limiting
STATUS_REFRESHED,
USER_CANCELLED
} StatusCode;
@@ -78,15 +76,9 @@ public:
* Notification when catalogs/packages are added or removed
*/
virtual void availablePackagesChanged() {}
/**
* More general purpose notification when install is queued / cancelled / started
* stopped. Reason value is only in certain cases.
*/
virtual void installStatusChanged(InstallRef aInstall, StatusCode aReason);
virtual void dataForThumbnail(const std::string& aThumbnailUrl,
size_t lenth, const uint8_t* bytes);
virtual void dataForThumbnail(const std::string& aThumbnailUrl,
size_t lenth, const uint8_t* bytes) {}
};
} // of namespace pkg

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>
@@ -268,11 +266,6 @@ string_set Package::tags() const
{
return m_tags;
}
bool Package::hasTag(const std::string& tag) const
{
return m_tags.find(tag) != m_tags.end();
}
SGPropertyNode* Package::properties() const
{
@@ -281,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
@@ -296,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;
@@ -417,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,55 +101,43 @@ 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;
/**
* @brief hasTag - efficently check if a tag is defined or not
* @param tag
* @return
*/
bool hasTag(const std::string& tag) const;
/**
* 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
@@ -191,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,
@@ -204,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);
@@ -236,7 +203,7 @@ private:
string_set m_tags;
CatalogRef m_catalog;
string_list m_variants;
mutable function_list<InstallCallback> _install_cb;
};
@@ -248,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>
@@ -30,50 +28,27 @@
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/sg_hash.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/package/Package.hxx>
#include <simgear/package/Install.hxx>
#include <simgear/package/Catalog.hxx>
const int SECONDS_PER_DAY = 24 * 60 * 60;
namespace simgear {
namespace {
std::string hashForUrl(const std::string& d)
{
sha1nfo info;
sha1_init(&info);
sha1_write(&info, d.data(), d.size());
return strutils::encodeHex(sha1_result(&info), HASH_LENGTH);
}
} // of anonymous namespace
namespace pkg {
typedef std::map<std::string, CatalogRef> CatalogDict;
typedef std::vector<Delegate*> DelegateVec;
typedef std::map<std::string, std::string> MemThumbnailCache;
typedef std::deque<std::string> StringDeque;
class Root::ThumbnailDownloader : public HTTP::Request
{
public:
ThumbnailDownloader(Root::RootPrivate* aOwner,
const std::string& aUrl, const std::string& aRealUrl = std::string()) :
HTTP::Request(aUrl),
m_owner(aOwner),
m_realUrl(aRealUrl)
ThumbnailDownloader(Root::RootPrivate* aOwner, const std::string& aUrl) :
HTTP::Request(aUrl),
m_owner(aOwner)
{
if (m_realUrl.empty()) {
m_realUrl = aUrl;
}
}
std::string realUrl() const
{
return m_realUrl;
}
protected:
@@ -87,7 +62,6 @@ protected:
private:
Root::RootPrivate* m_owner;
std::string m_buffer;
std::string m_realUrl;
};
class Root::RootPrivate
@@ -95,90 +69,69 @@ class Root::RootPrivate
public:
RootPrivate() :
http(NULL),
maxAgeSeconds(SECONDS_PER_DAY)
maxAgeSeconds(60 * 60 * 24)
{
}
void fireStatusChange(InstallRef install, Delegate::StatusCode status)
{
for (auto d : delegates) {
d->installStatusChanged(install, status);
}
}
void fireStartInstall(InstallRef install)
{
for (auto d : delegates) {
d->startInstall(install);
d->installStatusChanged(install, Delegate::STATUS_IN_PROGRESS);
DelegateVec::const_iterator it;
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->startInstall(install);
}
}
void fireInstallProgress(InstallRef install,
unsigned int aBytes, unsigned int aTotal)
{
for (auto d : delegates) {
d->installProgress(install, aBytes, aTotal);
DelegateVec::const_iterator it;
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->installProgress(install, aBytes, aTotal);
}
}
void fireFinishInstall(InstallRef install, Delegate::StatusCode status)
{
for (auto d : delegates) {
d->finishInstall(install, status);
d->installStatusChanged(install, status);
DelegateVec::const_iterator it;
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->finishInstall(install, status);
}
}
void fireRefreshStatus(CatalogRef catalog, Delegate::StatusCode status)
{
for (auto d : delegates) {
d->catalogRefreshed(catalog, status);
DelegateVec::const_iterator it;
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->catalogRefreshed(catalog, status);
}
}
void firePackagesChanged()
{
for (auto d : delegates) {
d->availablePackagesChanged();
}
DelegateVec::const_iterator it;
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->availablePackagesChanged();
}
}
void thumbnailDownloadComplete(HTTP::Request_ptr request,
Delegate::StatusCode status, const std::string& bytes)
{
auto dl = static_cast<Root::ThumbnailDownloader*>(request.get());
std::string u = dl->realUrl();
std::string u(request->url());
if (status == Delegate::STATUS_SUCCESS) {
thumbnailCache[u].requestPending = false;
fireDataForThumbnail(u, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
// if this was a network load, rather than a re-load from the disk cache,
// then persist to disk now.
if (strutils::starts_with(request->url(), "http")) {
addToPersistentCache(u, bytes);
}
} else if (status == Delegate::FAIL_HTTP_FORBIDDEN) {
// treat this as rate-limiting failure, at least from some mirrors
// (eg Ibiblio) and retry up to the max count
const int retries = (thumbnailCache[u].retryCount++);
if (retries < 3) {
SG_LOG(SG_IO, SG_INFO, "Download failed for: " << u << ", will retry");
thumbnailCache[u].requestPending = true;
pendingThumbnails.push_back(u);
}
} else {
// any other failure.
thumbnailCache[u].requestPending = false;
thumbnailCache[u] = bytes;
fireDataForThumbnail(u, bytes);
}
downloadNextPendingThumbnail();
}
void fireDataForThumbnail(const std::string& aUrl, const uint8_t* bytes, size_t size)
void fireDataForThumbnail(const std::string& aUrl, const std::string& bytes)
{
for (auto d : delegates) {
d->dataForThumbnail(aUrl, size, bytes);
DelegateVec::const_iterator it;
const uint8_t* data = reinterpret_cast<const uint8_t*>(bytes.data());
for (it = delegates.begin(); it != delegates.end(); ++it) {
(*it)->dataForThumbnail(aUrl, bytes.size(), data);
}
}
@@ -202,78 +155,10 @@ public:
void fireFinishUninstall(PackageRef pkg)
{
for (auto d : delegates) {
d->finishUninstall(pkg);
}
std::for_each(delegates.begin(), delegates.end(),
[pkg](Delegate* d) {d->finishUninstall(pkg);});
}
void addToPersistentCache(const std::string& url, const std::string& imageBytes)
{
std::string hash = hashForUrl(url);
// append the correct file suffix
auto pos = url.rfind('.');
if (pos == std::string::npos) {
return;
}
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
sg_ofstream fstream(cachePath, std::ios::out | std::ios::trunc | std::ios::binary);
fstream.write(imageBytes.data(), imageBytes.size());
fstream.close();
}
bool checkPersistentCache(const std::string& url)
{
std::string hash = hashForUrl(url);
// append the correct file suffix
auto pos = url.rfind('.');
if (pos == std::string::npos) {
return false;
}
SGPath cachePath = path / "ThumbnailCache" / (hash + url.substr(pos));
if (!cachePath.exists()) {
return false;
}
// check age, if it's too old, expire and download again
int age = time(nullptr) - cachePath.modTime();
if (age > SECONDS_PER_DAY * 7) { // cache for seven days
SG_LOG(SG_IO, SG_INFO, "expiring old cached thumbnail " << url);
cachePath.remove();
return false;
}
queueLoadFromPersistentCache(url, cachePath);
return true;
}
void queueLoadFromPersistentCache(const std::string& url, const SGPath& path)
{
assert(path.exists());
auto it = thumbnailCache.find(url);
if (it == thumbnailCache.end()) {
ThumbnailCacheEntry entry;
entry.pathOnDisk = path;
it = thumbnailCache.insert(it, std::make_pair(url, entry));
} else {
assert(it->second.pathOnDisk == path);
}
if (it->second.requestPending) {
return; // all done
}
it->second.requestPending = true;
auto dl = new Root::ThumbnailDownloader(this, path.fileUrl(), url);
if (http) {
http->makeRequest(dl);
} else {
httpPendingRequests.push_back(dl);
}
}
DelegateVec delegates;
SGPath path;
@@ -291,15 +176,7 @@ public:
HTTP::Request_ptr thumbnailDownloadRequest;
StringDeque pendingThumbnails;
struct ThumbnailCacheEntry
{
int retryCount = 0;
bool requestPending = false;
SGPath pathOnDisk;
};
std::map<std::string, ThumbnailCacheEntry> thumbnailCache;
MemThumbnailCache thumbnailCache;
typedef std::map<PackageRef, InstallRef> InstallCache;
InstallCache m_installs;
@@ -308,19 +185,16 @@ public:
void Root::ThumbnailDownloader::onDone()
{
if (simgear::strutils::starts_with(url(), "file://")) {
m_owner->thumbnailDownloadComplete(this, Delegate::STATUS_SUCCESS, m_buffer);
return;
}
if (responseCode() != 200) {
auto status = (responseCode() == 403) ? Delegate::FAIL_HTTP_FORBIDDEN : Delegate::FAIL_DOWNLOAD;
SG_LOG(SG_NETWORK, SG_INFO, "thumbnail download failure: " << url() << " with reason " << responseCode());
m_owner->thumbnailDownloadComplete(this, status, std::string());
SG_LOG(SG_GENERAL, SG_ALERT, "thumbnail download failure:" << url());
m_owner->thumbnailDownloadComplete(this, Delegate::FAIL_DOWNLOAD, std::string());
return;
}
m_owner->thumbnailDownloadComplete(this, Delegate::STATUS_SUCCESS, m_buffer);
//time(&m_owner->m_retrievedTime);
//m_owner->writeTimestamp();
//m_owner->refreshComplete(Delegate::STATUS_REFRESHED);
}
SGPath Root::path() const
@@ -384,14 +258,10 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
Dir dir(aPath);
if (!dir.exists()) {
dir.create(0755);
return;
}
Dir thumbsCacheDir(aPath / "ThumbnailCache");
if (!thumbsCacheDir.exists()) {
thumbsCacheDir.create(0755);
}
for (SGPath c : dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) {
BOOST_FOREACH(SGPath c, dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) {
CatalogRef cat = Catalog::createFromPath(this, c);
if (cat) {
if (cat->status() == Delegate::STATUS_SUCCESS) {
@@ -574,7 +444,7 @@ void Root::scheduleToUpdate(InstallRef aInstall)
}
PackageList deps = aInstall->package()->dependencies();
for (Package* dep : deps) {
BOOST_FOREACH(Package* dep, deps) {
// will internally schedule for update if required
// hence be careful, this method is re-entered in here!
dep->install();
@@ -583,7 +453,6 @@ void Root::scheduleToUpdate(InstallRef aInstall)
bool wasEmpty = d->updateDeque.empty();
d->updateDeque.push_back(aInstall);
d->fireStatusChange(aInstall, Delegate::STATUS_IN_PROGRESS);
if (wasEmpty) {
aInstall->startUpdate();
}
@@ -591,7 +460,8 @@ void Root::scheduleToUpdate(InstallRef aInstall)
bool Root::isInstallQueued(InstallRef aInstall) const
{
auto it = std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
RootPrivate::UpdateDeque::const_iterator it =
std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
return (it != d->updateDeque.end());
}
@@ -633,12 +503,11 @@ void Root::finishInstall(InstallRef aInstall, Delegate::StatusCode aReason)
void Root::cancelDownload(InstallRef aInstall)
{
auto it = std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
RootPrivate::UpdateDeque::iterator it =
std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
if (it != d->updateDeque.end()) {
bool startNext = (aInstall == d->updateDeque.front());
d->updateDeque.erase(it);
d->fireStatusChange(aInstall, Delegate::USER_CANCELLED);
if (startNext) {
if (!d->updateDeque.empty()) {
d->updateDeque.front()->startUpdate();
@@ -738,21 +607,17 @@ bool Root::removeCatalogById(const std::string& aId)
void Root::requestThumbnailData(const std::string& aUrl)
{
auto it = d->thumbnailCache.find(aUrl);
MemThumbnailCache::iterator it = d->thumbnailCache.find(aUrl);
if (it == d->thumbnailCache.end()) {
bool cachedOnDisk = d->checkPersistentCache(aUrl);
if (cachedOnDisk) {
// checkPersistentCache will insert the entry and schedule
} else {
d->pendingThumbnails.push_front(aUrl);
d->thumbnailCache[aUrl] = RootPrivate::ThumbnailCacheEntry();
d->thumbnailCache[aUrl].requestPending = true;
d->downloadNextPendingThumbnail();
}
// insert into cache to mark as pending
d->pendingThumbnails.push_front(aUrl);
d->thumbnailCache[aUrl] = std::string();
d->downloadNextPendingThumbnail();
} else if (!it->second.empty()) {
// already loaded, fire data synchronously
d->fireDataForThumbnail(aUrl, it->second);
} else {
if (!it->second.requestPending && it->second.pathOnDisk.exists()) {
d->queueLoadFromPersistentCache(aUrl, it->second.pathOnDisk);
}
// in cache but empty data, still fetching
}
}

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>

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