diff --git a/.gitignore b/.gitignore index 590b23926f..69abe464d4 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ gui_test slic3r_test compile_commands.json tags +.vscode diff --git a/Build.PL b/Build.PL index f928915908..c024020d9d 100755 --- a/Build.PL +++ b/Build.PL @@ -149,13 +149,13 @@ EOF if (!$gui) { # clean xs directory before reinstalling, to make sure Build is called # with current perl binary - if (-e './xs/Build') { - if ($^O eq 'MSWin32') { - system '.\xs\Build', 'distclean'; - } else { - system './xs/Build', 'distclean'; - } - } + #if (-e './xs/Build') { + # if ($^O eq 'MSWin32') { + # system '.\xs\Build', 'distclean'; + # } else { + # system './xs/Build', 'distclean'; + # } + #} my $res = system $cpanm, @cpanm_args, '--reinstall', '--verbose', './xs'; if ($res != 0) { die "The XS/C++ code failed to compile, aborting\n"; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 27c1bed4e3..e7fbf749a8 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -17,6 +17,25 @@ use Slic3r::Print::State ':steps'; use Slic3r::Surface qw(S_TYPE_BOTTOM); our $status_cb; +our $travel_height; +our $enable_loop_clipping; + +sub set_travel_height { + my ($class, $th) = @_; + $travel_height = $th; +} +sub travel_height { + return $travel_height; +} + +sub set_enable_loop_clipping { + my ($class, $th) = @_; + $enable_loop_clipping = $th; +} + +sub enable_loop_clipping { + return $enable_loop_clipping; +} sub set_status_cb { my ($class, $cb) = @_; diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 0cd16cc321..c0f037569f 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -15,6 +15,7 @@ has '_brim_done' => (is => 'rw'); has '_second_layer_things_done' => (is => 'rw'); has '_last_obj_copy' => (is => 'rw'); has '_autospeed' => (is => 'rw', default => sub { 0 }); # boolean +has '_enable_loop_clipping' => (is => 'rw'); use List::Util qw(first sum min max); use Slic3r::ExtrusionPath ':roles'; @@ -139,6 +140,8 @@ sub export { # but we need it for skirt/brim too $gcodegen->set_first_layer(1); + $self->_enable_loop_clipping($self->print->enable_loop_clipping); + # disable fan print $fh $gcodegen->writer->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers; @@ -239,21 +242,23 @@ sub export { # move to the origin position for the copy we're going to print. # this happens before Z goes down to layer 0 again, so that # no collision happens hopefully. - if ($finished_objects > 0) { - $gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); - $gcodegen->set_enable_cooling_markers(0); # we're not filtering these moves through CoolingBuffer - $gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1); - print $fh $gcodegen->retract; - print $fh $gcodegen->travel_to( - Slic3r::Point->new(0,0), - EXTR_ROLE_NONE, - 'move to origin position for next object', - ); - $gcodegen->set_enable_cooling_markers(1); - - # disable motion planner when traveling to first object point - $gcodegen->avoid_crossing_perimeters->set_disable_once(1); + $gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); + $gcodegen->set_enable_cooling_markers(0); # we're not filtering these moves through CoolingBuffer + $gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1); + print $fh $gcodegen->retract; + if ($self->print->travel_height) { + printf $fh "G1 Z%.f 1200 ; Move the bed down to the specified height when traveling between complete objects\n", + $self->print->travel_height; } + print $fh $gcodegen->travel_to( + Slic3r::Point->new(0,0), + EXTR_ROLE_NONE, + 'move to origin position for next object', + ); + $gcodegen->set_enable_cooling_markers(1); + + # disable motion planner when traveling to first object point + $gcodegen->avoid_crossing_perimeters->set_disable_once(1); my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers}; for my $layer (@layers) { @@ -411,7 +416,8 @@ sub process_layer { } # if we're going to apply spiralvase to this layer, disable loop clipping - $self->_gcodegen->set_enable_loop_clipping(!defined $self->_spiral_vase || !$self->_spiral_vase->enable); + # also disable loop clipping if it has been explicitly disabled + $self->_gcodegen->set_enable_loop_clipping((!defined $self->_spiral_vase || !$self->_spiral_vase->enable) && $self->_enable_loop_clipping); # initialize autospeed { diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 5f155e9dae..7bc913008c 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -67,6 +67,14 @@ has 'output_file' => ( is => 'rw', ); +has 'travel_height' => ( + is => 'rw', +); + +has 'disable_loop_clipping' => ( + is => 'rw', +); + sub _bed_polygon { my ($self) = @_; @@ -135,6 +143,14 @@ sub _before_export { my ($self) = @_; $self->_print->set_status_cb($self->status_cb); + if ($self->travel_height) { + $self->_print->set_travel_height($self->travel_height); + } + if ($self->disable_loop_clipping) { + $self->_print->set_enable_loop_clipping(0); + } else { + $self->_print->set_enable_loop_clipping(1); + } $self->_print->validate; } diff --git a/package/win/compile_wrapper.ps1 b/package/win/compile_wrapper.ps1 index 44fdfe90b3..94cff8642c 100644 --- a/package/win/compile_wrapper.ps1 +++ b/package/win/compile_wrapper.ps1 @@ -1,8 +1,8 @@ # Short Powershell script to build a wrapper exec Param ( - [string]$perlVersion = "524", - [string]$STRAWBERRY_PATH = "C:\Strawberry", + [string]$perlVersion = "532", + [string]$STRAWBERRY_PATH = "C:\StrawberryPerl", # Path to C++ compiler, or just name if it is in path [string]$cxx = "g++" ) diff --git a/package/win/slic3r.exe.manifest b/package/win/slic3r.exe.manifest index 40c7d9e6b3..f7cae7307b 100644 --- a/package/win/slic3r.exe.manifest +++ b/package/win/slic3r.exe.manifest @@ -29,4 +29,9 @@ + + + 1252 + + diff --git a/slic3r.pl b/slic3r.pl index 16531fc401..ba802960c8 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -36,6 +36,7 @@ BEGIN 'save=s' => \$opt{save}, 'load=s@' => \$opt{load}, + 'lm=s@' => \$opt{lm}, 'autosave=s' => \$opt{autosave}, 'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config}, 'datadir=s' => \$opt{datadir}, @@ -51,11 +52,23 @@ BEGIN 'rotate=f' => \$opt{rotate}, 'rotate-x=f' => \$opt{rotate_x}, 'rotate-y=f' => \$opt{rotate_y}, + 'sc=f@' => \$opt{sc}, + 'sx=f@' => \$opt{sx}, + 'sy=f@' => \$opt{sy}, + 'sz=f@' => \$opt{sz}, + 'rx=f@' => \$opt{rx}, + 'ry=f@' => \$opt{ry}, + 'rz=f@' => \$opt{rz}, + 'tx=f@' => \$opt{tx}, + 'ty=f@' => \$opt{ty}, + 'tz=f@' => \$opt{tz}, 'duplicate=i' => \$opt{duplicate}, 'duplicate-grid=s' => \$opt{duplicate_grid}, 'print-center=s' => \$opt{print_center}, 'dont-arrange' => \$opt{dont_arrange}, - + 'travel-height=f' => \$opt{travel_height}, + 'disable-loop-clipping' => \$opt{disable_loop_clipping}, + # legacy options, ignored 'no-plater' => \$opt{no_plater}, 'gui-mode=s' => \$opt{gui_mode}, @@ -92,6 +105,25 @@ BEGIN $_->normalize for @external_configs; } +# load model configuration files +my @model_configs = (); +if ($opt{lm}) { + foreach my $configfile (@{$opt{lm}}) { + $configfile = Slic3r::decode_path($configfile); + if (-e Slic3r::encode_path($configfile)) { + push @model_configs, Slic3r::Config->load($configfile); + } elsif (-e Slic3r::encode_path("$FindBin::Bin/$configfile")) { + printf STDERR "Loading $FindBin::Bin/$configfile\n"; + push @model_configs, Slic3r::Config->load("$FindBin::Bin/$configfile"); + } else { + $opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n"; + } + } + + # expand shortcuts before applying, otherwise destination values would be already filled with defaults + $_->normalize for @model_configs; +} + # process command line options my $cli_config = Slic3r::Config->new_from_cli(%cli_options); $cli_config->normalize; # expand shortcuts @@ -240,6 +272,22 @@ BEGIN my $model; if ($opt{merge}) { my @models = map Slic3r::Model->read_from_file($_), $input_file, (splice @ARGV, 0); + foreach my $i (0 .. $#models) { + foreach my $instance (@{$models[$i]->objects}) { + $instance->rotate(deg2rad($opt{rx}[$i] // 0), X); + $instance->rotate(deg2rad($opt{ry}[$i] // 0), Y); + $instance->rotate(deg2rad($opt{rz}[$i] // 0), Z); + my $sc = $opt{sc}[$i] // 1; + $instance->scale_xyz(Slic3r::Pointf3->new($opt{sx}[$i] // $sc, $opt{sy}[$i] // $sc, $opt{sz}[$i] // $sc)); + $instance->translate($opt{tx}[$i] // 0, $opt{ty}[$i] // 0, $opt{tz}[$i] // 0); + if ($model_configs[$i]) { + my $config = Slic3r::Config->new_from_defaults; + $config->apply($model_configs[$i]); + $config->validate; + $instance->config()->apply($config); + } + } + } $model = Slic3r::Model->merge(@models); } else { $model = Slic3r::Model->read_from_file($input_file); @@ -267,6 +315,8 @@ BEGIN duplicate_grid => $opt{duplicate_grid} // [1,1], print_center => $opt{print_center}, dont_arrange => $opt{dont_arrange} // 0, + travel_height => $opt{travel_height}, + disable_loop_clipping => $opt{disable_loop_clipping}, status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; @@ -475,6 +525,7 @@ sub usage { --infill-only-where-needed Only infill under ceilings (default: no) --infill-first Make infill before perimeters (default: no) + --disable-loop-clipping Clip ends of loops to avoid overlapping extrusion (default: no) Quality options (slower slicing): --extra-perimeters Add more perimeters when needed (default: yes) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b3ad7ed8a..02934c0356 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -247,7 +247,11 @@ target_compile_features(libslic3r PUBLIC cxx_std_11) target_include_directories(libslic3r SYSTEM PUBLIC ${SLIC3R_INCLUDES}) target_include_directories(libslic3r PUBLIC ${LIBSLIC3R_INCLUDES}) if (BOOST_NOWIDE_FOUND) - target_compile_options(libslic3r -DBOOST_INCLUDE_NOWIDE) + target_compile_definitions(libslic3r PUBLIC BOOST_INCLUDE_NOWIDE=1 BOOST_NOWIDE_FOUND=1) +endif() + +if(NOT(${Boost_VERSION_STRING} VERSION_LESS "1.74.0")) + target_compile_definitions(libslic3r PUBLIC BOOST_BIND_GLOBAL_PLACEHOLDERS=1) endif() add_library(BSpline STATIC diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 8ed723f367..15ab90451f 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -1,3 +1,5 @@ +#include // include winsock2.h first + #include "slic3r.hpp" #include "GCodeSender.hpp" #include "Geometry.hpp" @@ -8,19 +10,21 @@ #include "SimplePrint.hpp" #include "TriangleMesh.hpp" #include "libslic3r.h" -#include + +#include +#include +#include #include +#include #include -#include #include +#include #include #include #include -#include -#include -#include -#include #include +#include +#include #include #ifdef USE_WX diff --git a/src/test/libslic3r/test_trianglemesh.cpp b/src/test/libslic3r/test_trianglemesh.cpp index c4aafd4fd0..c5b984ba51 100644 --- a/src/test/libslic3r/test_trianglemesh.cpp +++ b/src/test/libslic3r/test_trianglemesh.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace Slic3r; using namespace std; diff --git a/xs/Build.PL b/xs/Build.PL index fabbf9339b..9c6b7e145e 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -18,7 +18,7 @@ $ENV{LD_RUN_PATH} //= ""; # HAS_BOOL : stops Perl/lib/CORE/handy.h from doing "# define bool char" for MSVC # NOGDI : prevents inclusion of wingdi.h which defines functions Polygon() and Polyline() in global namespace # BOOST_ASIO_DISABLE_KQUEUE : prevents a Boost ASIO bug on OS X: https://svn.boost.org/trac/boost/ticket/5339 -my @cflags = qw(-DHAS_BOOL -DNOGDI -DSLIC3RXS -DBOOST_ASIO_DISABLE_KQUEUE -Dexprtk_disable_rtl_io_file -Dexprtk_disable_return_statement -Dexprtk_disable_rtl_vecops -Dexprtk_disable_string_capabilities -Dexprtk_disable_enhanced_features); +my @cflags = qw(-DHAS_BOOL -DNOGDI -DSLIC3RXS -DBOOST_ASIO_DISABLE_KQUEUE -DBOOST_NOWIDE_FOUND -DBOOST_NOWIDE_INCLUDE -Dexprtk_disable_rtl_io_file -Dexprtk_disable_return_statement -Dexprtk_disable_rtl_vecops -Dexprtk_disable_string_capabilities -Dexprtk_disable_enhanced_features); push @cflags, "-DSLIC3R_BUILD_COMMIT=$ENV{SLIC3R_GIT_VERSION}" if defined $ENV{SLIC3R_GIT_VERSION}; # std=c++11 Enforce usage of C++11 (required now). Minimum compiler supported: gcc 4.9, clang 3.3, MSVC 14.0 @@ -77,150 +77,15 @@ if ($^O eq 'darwin') { } } if ($mswin) { - push @cflags, qw(-DNOMINMAX); + push @cflags, qw(-DNOMINMAX -DBOOST_ASIO_ENABLE_OLD_SERVICES); } my @INC = qw(-Isrc/libslic3r); my @LIBS = $cpp_guess->is_msvc ? qw(-LIBPATH:src/libslic3r) : qw(-Lsrc/libslic3r); -# search for Boost in a number of places -my @boost_include = (); -if (defined $ENV{BOOST_INCLUDEDIR}) { - push @boost_include, $ENV{BOOST_INCLUDEDIR} -} elsif (defined $ENV{BOOST_DIR}) { - # User might have provided a path relative to the Build.PL in the main directory - foreach my $subdir ($ENV{BOOST_DIR}, "../$ENV{BOOST_DIR}", "$ENV{BOOST_DIR}/include", "../$ENV{BOOST_DIR}/include") { - next if $subdir =~ m{^\.\.//}; - printf "Checking %s: ", $subdir; - if (-d $subdir) { - push @boost_include, $subdir; - print "found\n"; - } else { - print "not found\n"; - } - } - die "Invalid BOOST_DIR: could not find Boost headers\n" if !@boost_include; -} else { - # Boost library was not defined by the environment. - # Try to guess at some default paths. - if ($mswin) { - for my $path (glob('C:\dev\boost*\include'), glob ('C:\boost*\include')) { - push @boost_include, $path; - } - if (! @boost_include) { - # No boost\include. Try to include the boost root. - for my $path (glob('C:\dev\boost*'), glob ('C:\boost*')) { - push @boost_include, $path; - } - } - } else { - push @boost_include, grep { -d $_ } - qw(/opt/local/include /usr/local/include /opt/include /usr/include); - } -} - -my @boost_libs = (); -if (defined $ENV{BOOST_LIBRARYPATH}) { - push @boost_libs, $ENV{BOOST_LIBRARYPATH} -} elsif (defined $ENV{BOOST_DIR}) { - # User might have provided a path relative to the Build.PL in the main directory - foreach my $subdir ("$ENV{BOOST_DIR}/stage/lib", "../$ENV{BOOST_DIR}/stage/lib", "$ENV{BOOST_DIR}/lib", "../$ENV{BOOST_DIR}/lib") { - next if $subdir =~ m{^\.\.//}; - printf "Checking %s: ", $subdir; - if (-d $subdir) { - push @boost_libs, $subdir; - print "found\n"; - } else { - print "not found\n"; - } - } - die "Invalid BOOST_DIR: could not find Boost libs\n" if !@boost_libs; -} else { - # Boost library was not defined by the environment. - # Try to guess at some default paths. - if ($mswin) { - for my $path ( - glob('C:\dev\boost*\lib'), glob ('C:\boost*\lib'), - glob('C:\dev\boost*\stage\lib'), glob ('C:\boost*\stage\lib')) { - push @boost_libs, $path; - } - } else { - push @boost_libs, grep { -d $_ } - qw(/opt/local/lib /usr/local/lib /opt/lib /usr/lib /lib); - } -} -# In order to generate the -l switches we need to know how Boost libraries are named -my $have_boost = 0; -my $have_boost_optional = 0; -my @boost_libraries = qw(system thread filesystem); # we need these -my @boost_optional_libraries = qw(nowide); # we need these, but if they aren't present we can deal -# check without explicit lib path (works on Linux) -if (! $mswin) { - $have_boost = 1 - if check_lib( - lib => [ map "boost_${_}", @boost_libraries ], - ); - $have_boost_optional = 1 - if check_lib( - lib => [ map "boost_${_}", @boost_optional_libraries ], - ); -} - -if (!$ENV{SLIC3R_STATIC} && $have_boost) { - # The boost library was detected by check_lib on Linux. - push @LIBS, map "-lboost_${_}", @boost_libraries; - if (!$ENV{SLIC3R_STATIC} && $have_boost_optional) { - push @LIBS, map "-lboost_${_}", @boost_optional_libraries; - } -} else { - # Either static linking, or check_lib could not be used to find the boost libraries. - my $lib_prefix = 'libboost_'; - my $lib_ext = $Config{lib_ext}; - PATH: foreach my $path (@boost_libs) { - # Try to find the boost system library. - my @files = glob "$path/${lib_prefix}system*$lib_ext"; - next if !@files; - - if ($files[0] =~ /${lib_prefix}system([^.]*)$lib_ext$/) { - # Suffix contains the version number, the build type etc. - my $suffix = $1; - # Verify existence of all required boost libraries at $path. - for my $lib (map "${lib_prefix}${_}${suffix}${lib_ext}", @boost_libraries) { - # If the library file does not exist, try next library path. - -f "$path/$lib" or next PATH; - } - if (! $cpp_guess->is_msvc) { - # Test the correctness of boost libraries by linking them to a minimal C program. - check_lib( - lib => [ map "boost_${_}${suffix}", @boost_libraries ], - INC => join(' ', map "-I$_", @INC, @boost_include), - LIBS => "-L$path", - ) or next; - } - push @INC, (map " -I$_", @boost_include); # TODO: only use the one related to the chosen lib path - if ($ENV{SLIC3R_STATIC} || $cpp_guess->is_msvc) { - push @LIBS, map "${path}/${lib_prefix}$_${suffix}${lib_ext}", @boost_libraries; - } else { - push @LIBS, " -L$path", (map " -lboost_$_$suffix", @boost_libraries); - } - $have_boost = 1; - last; - } - } -} -die <<'EOF' if !$have_boost; -Slic3r requires the Boost libraries. Please make sure they are installed. - -If they are installed, this script should be able to locate them in several -standard locations. If this is not the case, you might want to supply their -path through the BOOST_INCLUDEDIR and BOOST_LIBRARYPATH environment variables: - - BOOST_INCLUDEDIR=/usr/local/include BOOST_LIBRARYPATH=/usr/lib perl Build.PL - -If you just compiled Boost in its source directory without installing it in the -system you can just provide the BOOST_DIR variable pointing to that directory. - -EOF +push @cflags, '-DBOOST_LIBS'; +my @boost_libraries = qw(system thread filesystem nowide); # we need these +push @LIBS, map "-lboost_${_}", @boost_libraries; if ($ENV{SLIC3R_DEBUG}) { # only on newer GCCs: -ftemplate-backtrace-limit=0 diff --git a/xs/src/boost/nowide/args.hpp b/xs/src/boost/nowide/args.hpp old mode 100755 new mode 100644 index eb483c245b..e11b72cf7a --- a/xs/src/boost/nowide/args.hpp +++ b/xs/src/boost/nowide/args.hpp @@ -2,75 +2,81 @@ // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_NOWIDE_ARGS_HPP_INCLUDED #define BOOST_NOWIDE_ARGS_HPP_INCLUDED #include -#include -#include #ifdef BOOST_WINDOWS +#include #include +#include +#include #endif namespace boost { namespace nowide { - #if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) - class args { +#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) + class args + { public: - args(int &,char **&) {} - args(int &,char **&,char **&){} - ~args() {} + args(int&, char**&) + {} + args(int&, char**&, char**&) + {} }; - #else +#else /// - /// \brief args is a class that fixes standard main() function arguments and changes them to UTF-8 under - /// Microsoft Windows. + /// \brief \c args is a class that temporarily replaces standard main() function arguments with their + /// equal, but UTF-8 encoded values under Microsoft Windows for the lifetime of the instance. /// /// The class uses \c GetCommandLineW(), \c CommandLineToArgvW() and \c GetEnvironmentStringsW() - /// in order to obtain the information. It does not relates to actual values of argc,argv and env - /// under Windows. + /// in order to obtain Unicode-encoded values. + /// It does not relate to actual values of argc, argv and env under Windows. + /// + /// It restores the original values in its destructor (usually at the end of the \c main function). /// - /// It restores the original values in its destructor + /// If any of the system calls fails, an exception of type std::runtime_error will be thrown + /// and argc, argv, env remain unchanged. /// - /// \note the class owns the memory of the newly allocated strings + /// \note The class owns the memory of the newly allocated strings. + /// So you need to keep it alive as long as you use the values. /// - class args { + /// Usage: + /// \code + /// int main(int argc, char** argv, char** env) { + /// boost::nowide::args _(argc, argv, env); // Note the _ as a "don't care" name for the instance + /// // Use argv and env as usual, they are now UTF-8 encoded on Windows + /// return 0; // Memory held by args is released + /// } + /// \endcode + class args + { public: - /// - /// Fix command line arguments + /// Fix command line arguments /// - args(int &argc,char **&argv) : - old_argc_(argc), - old_argv_(argv), - old_env_(0), - old_argc_ptr_(&argc), - old_argv_ptr_(&argv), - old_env_ptr_(0) + args(int& argc, char**& argv) : + old_argc_(argc), old_argv_(argv), old_env_(0), old_argc_ptr_(&argc), old_argv_ptr_(&argv), old_env_ptr_(0) { - fix_args(argc,argv); + fix_args(argc, argv); } /// /// Fix command line arguments and environment /// - args(int &argc,char **&argv,char **&en) : - old_argc_(argc), - old_argv_(argv), - old_env_(en), - old_argc_ptr_(&argc), - old_argv_ptr_(&argv), - old_env_ptr_(&en) + args(int& argc, char**& argv, char**& env) : + old_argc_(argc), old_argv_(argv), old_env_(env), old_argc_ptr_(&argc), old_argv_ptr_(&argv), + old_env_ptr_(&env) { - fix_args(argc,argv); - fix_env(en); + fix_args(argc, argv); + fix_env(env); } /// - /// Restore original argc,argv,env values, if changed + /// Restore original argc, argv, env values, if changed /// ~args() { @@ -78,90 +84,113 @@ namespace nowide { *old_argc_ptr_ = old_argc_; if(old_argv_ptr_) *old_argv_ptr_ = old_argv_; - if(old_env_ptr_) + if(old_env_ptr_) *old_env_ptr_ = old_env_; } - private: - void fix_args(int &argc,char **&argv) + + private: + class wargv_ptr { - int wargc; - wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(),&wargc); - if(!wargv) { - argc = 0; - static char *dummy = 0; - argv = &dummy; - return; + wchar_t** p; + int argc; + + public: + wargv_ptr() + { + p = CommandLineToArgvW(GetCommandLineW(), &argc); } - try{ - args_.resize(wargc+1,0); - arg_values_.resize(wargc); - for(int i=0;i args_; + std::vector args_; std::vector arg_values_; stackstring env_; - std::vector envp_; + std::vector envp_; int old_argc_; - char **old_argv_; - char **old_env_; + char** old_argv_; + char** old_env_; - int *old_argc_ptr_; - char ***old_argv_ptr_; - char ***old_env_ptr_; + int* old_argc_ptr_; + char*** old_argv_ptr_; + char*** old_env_ptr_; }; - #endif +#endif -} // nowide +} // namespace nowide } // namespace boost #endif - -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/cenv.hpp b/xs/src/boost/nowide/cenv.hpp deleted file mode 100755 index 90ce7b86fa..0000000000 --- a/xs/src/boost/nowide/cenv.hpp +++ /dev/null @@ -1,128 +0,0 @@ -// -// Copyright (c) 2012 Artyom Beilis (Tonkikh) -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef BOOST_NOWIDE_CENV_H_INCLUDED -#define BOOST_NOWIDE_CENV_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -#ifdef BOOST_WINDOWS -#include -#endif - -namespace boost { -namespace nowide { - #if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) - using ::getenv; - using ::setenv; - using ::unsetenv; - using ::putenv; - #else - /// - /// \brief UTF-8 aware getenv. Returns 0 if the variable is not set. - /// - /// This function is not thread safe or reenterable as defined by the standard library - /// - inline char *getenv(char const *key) - { - static stackstring value; - - wshort_stackstring name; - if(!name.convert(key)) - return 0; - - static const size_t buf_size = 64; - wchar_t buf[buf_size]; - std::vector tmp; - wchar_t *ptr = buf; - size_t n = GetEnvironmentVariableW(name.c_str(),buf,buf_size); - if(n == 0 && GetLastError() == 203) // ERROR_ENVVAR_NOT_FOUND - return 0; - if(n >= buf_size) { - tmp.resize(n+1,L'\0'); - n = GetEnvironmentVariableW(name.c_str(),&tmp[0],static_cast(tmp.size() - 1)); - // The size may have changed - if(n >= tmp.size() - 1) - return 0; - ptr = &tmp[0]; - } - if(!value.convert(ptr)) - return 0; - return value.c_str(); - } - /// - /// \brief UTF-8 aware setenv, \a key - the variable name, \a value is a new UTF-8 value, - /// - /// if override is not 0, that the old value is always overridden, otherwise, - /// if the variable exists it remains unchanged - /// - inline int setenv(char const *key,char const *value,int override) - { - wshort_stackstring name; - if(!name.convert(key)) - return -1; - if(!override) { - wchar_t unused[2]; - if(!(GetEnvironmentVariableW(name.c_str(),unused,2)==0 && GetLastError() == 203)) // ERROR_ENVVAR_NOT_FOUND - return 0; - } - wstackstring wval; - if(!wval.convert(value)) - return -1; - if(SetEnvironmentVariableW(name.c_str(),wval.c_str())) - return 0; - return -1; - } - /// - /// \brief Remove environment variable \a key - /// - inline int unsetenv(char const *key) - { - wshort_stackstring name; - if(!name.convert(key)) - return -1; - if(SetEnvironmentVariableW(name.c_str(),0)) - return 0; - return -1; - } - /// - /// \brief UTF-8 aware putenv implementation, expects string in format KEY=VALUE - /// - inline int putenv(char *string) - { - if (string == nullptr) return -1; - char const *key = string; - char const *key_end = string; - - while(*key_end!='=' && *key_end!='\0') - key_end++; - if(*key_end == '\0') - return -1; - wshort_stackstring wkey; - if(!wkey.convert(key,key_end)) - return -1; - - wstackstring wvalue; - if(!wvalue.convert(key_end+1)) - return -1; - - if(SetEnvironmentVariableW(wkey.c_str(),wvalue.c_str())) - return 0; - return -1; - } - #endif -} // nowide -} // namespace boost - -#endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/config.hpp b/xs/src/boost/nowide/config.hpp old mode 100755 new mode 100644 index d983109f38..be7dc6115f --- a/xs/src/boost/nowide/config.hpp +++ b/xs/src/boost/nowide/config.hpp @@ -1,36 +1,35 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2019 - 2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_NOWIDE_CONFIG_HPP_INCLUDED #define BOOST_NOWIDE_CONFIG_HPP_INCLUDED +/// @file + #include +#include +#include -#ifndef BOOST_SYMBOL_VISIBLE -# define BOOST_SYMBOL_VISIBLE -#endif +//! @cond Doxygen_Suppress -#ifdef BOOST_HAS_DECLSPEC -# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK) -# ifdef BOOST_NOWIDE_SOURCE -# define BOOST_NOWIDE_DECL BOOST_SYMBOL_EXPORT -# else -# define BOOST_NOWIDE_DECL BOOST_SYMBOL_IMPORT -# endif // BOOST_NOWIDE_SOURCE -# endif // DYN_LINK -#endif // BOOST_HAS_DECLSPEC - -#ifndef BOOST_NOWIDE_DECL -# define BOOST_NOWIDE_DECL -#endif +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK) +#ifdef BOOST_NOWIDE_SOURCE +#define BOOST_NOWIDE_DECL BOOST_SYMBOL_EXPORT +#else +#define BOOST_NOWIDE_DECL BOOST_SYMBOL_IMPORT +#endif // BOOST_NOWIDE_SOURCE +#else +#define BOOST_NOWIDE_DECL +#endif // BOOST_NOWIDE_DYN_LINK // -// Automatically link to the correct build variant where possible. -// +// Automatically link to the correct build variant where possible. +// #if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_NOWIDE_NO_LIB) && !defined(BOOST_NOWIDE_SOURCE) // // Set the name of our library, this will get undef'ed by auto_link.hpp @@ -41,14 +40,79 @@ // If we're importing code from a dll, then tell auto_link.hpp about it: // #if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_NOWIDE_DYN_LINK) -# define BOOST_DYN_LINK +#define BOOST_DYN_LINK #endif // // And include the header that does the work: // #include -#endif // auto-linking disabled +#endif // auto-linking disabled + +//! @endcond + +/// @def BOOST_NOWIDE_USE_WCHAR_OVERLOADS +/// @brief Whether to use the wchar_t* overloads in fstream/filebuf +/// Enabled on Windows and Cygwin as the latter may use wchar_t in filesystem::path +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 1 +#else +#define BOOST_NOWIDE_USE_WCHAR_OVERLOADS 0 +#endif + +/// @def BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +/// @brief Define to 1 to use internal class from filebuf.hpp +/// +/// - On Non-Windows platforms: Define to 1 to use the same class from header +/// that is used on Windows. +/// - On Windows: No effect, always overwritten to 1 +/// +/// Affects boost::nowide::basic_filebuf, +/// boost::nowide::basic_ofstream, boost::nowide::basic_ifstream, boost::nowide::basic_fstream +#if defined(BOOST_WINDOWS) || BOOST_NOWIDE_USE_WCHAR_OVERLOADS +#ifdef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#undef BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#endif +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 1 +#elif !defined(BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT) +#define BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT 0 +#endif + +//! @cond Doxygen_Suppress + +#if BOOST_VERSION < 106500 && defined(BOOST_GCC) && __GNUC__ >= 7 +#define BOOST_NOWIDE_FALLTHROUGH __attribute__((fallthrough)) +#else +#define BOOST_NOWIDE_FALLTHROUGH BOOST_FALLTHROUGH +#endif + +// The std::codecvt are deprecated in C++20 +// These macros can suppress this warning +#if defined(_MSC_VER) +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN __pragma(warning(push)) __pragma(warning(disable : 4996)) +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END __pragma(warning(pop)) +#elif(__cplusplus >= 202002L) && defined(__clang__) +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN \ + _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END _Pragma("clang diagnostic pop") +#elif(__cplusplus >= 202002L) && defined(__GNUC__) +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN \ + _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END _Pragma("GCC diagnostic pop") +#else +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN +#define BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END +#endif + +//! @endcond +namespace boost { +/// +/// \brief This namespace includes implementations of the standard library functions and +/// classes such that they accept UTF-8 strings on Windows. +/// On other platforms (i.e. not on Windows) those functions and classes are just aliases +/// of the corresponding ones from the std namespace or behave like them. +/// +namespace nowide {} +} // namespace boost #endif // boost/nowide/config.hpp -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 \ No newline at end of file diff --git a/xs/src/boost/nowide/convert.hpp b/xs/src/boost/nowide/convert.hpp old mode 100755 new mode 100644 index 89b8871d09..5e913d8a41 --- a/xs/src/boost/nowide/convert.hpp +++ b/xs/src/boost/nowide/convert.hpp @@ -1,154 +1,137 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_NOWIDE_CONVERT_H_INCLUDED -#define BOOST_NOWIDE_CONVERT_H_INCLUDED +#ifndef BOOST_NOWIDE_CONVERT_HPP_INCLUDED +#define BOOST_NOWIDE_CONVERT_HPP_INCLUDED +#include +#include #include -#include namespace boost { namespace nowide { + /// - /// \brief Template function that converts a buffer of UTF sequences in range [source_begin,source_end) - /// to the output \a buffer of size \a buffer_size. - /// - /// In case of success a NULL terminated string is returned (buffer), otherwise 0 is returned. + /// Convert wide string (UTF-16/32) in range [begin,end) to NULL terminated narrow string (UTF-8) + /// stored in \a output of size \a output_size (including NULL) /// - /// If there is not enough room in the buffer or the source sequence contains invalid UTF, - /// 0 is returned, and the contents of the buffer are undefined. + /// If there is not enough room NULL is returned, else output is returned. + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - template - CharOut *basic_convert(CharOut *buffer,size_t buffer_size,CharIn const *source_begin,CharIn const *source_end) + inline char* narrow(char* output, size_t output_size, const wchar_t* begin, const wchar_t* end) { - CharOut *rv = buffer; - if(buffer_size == 0) - return 0; - buffer_size --; - while(source_begin!=source_end) { - using namespace boost::locale::utf; - code_point c = utf_traits::template decode(source_begin,source_end); - if(c==illegal || c==incomplete) { - rv = 0; - break; - } - size_t width = utf_traits::width(c); - if(buffer_size < width) { - rv=0; - break; - } - buffer = utf_traits::template encode(c,buffer); - buffer_size -= width; - } - *buffer++ = 0; - return rv; + return utf::convert_buffer(output, output_size, begin, end); } - - /// \cond INTERNAL - namespace details { - // - // wcslen defined only in C99... So we will not use it - // - template - Char const *basic_strend(Char const *s) - { - while(*s) - s++; - return s; - } + /// + /// Convert NULL terminated wide string (UTF-16/32) to NULL terminated narrow string (UTF-8) + /// stored in \a output of size \a output_size (including NULL) + /// + /// If there is not enough room NULL is returned, else output is returned. + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// + inline char* narrow(char* output, size_t output_size, const wchar_t* source) + { + return narrow(output, output_size, source, source + utf::strlen(source)); } - /// \endcond /// - /// Convert NULL terminated UTF source string to NULL terminated \a output string of size at - /// most output_size (including NULL) - /// - /// In case of success output is returned, if the input sequence is illegal, - /// or there is not enough room NULL is returned + /// Convert narrow string (UTF-8) in range [begin,end) to NULL terminated wide string (UTF-16/32) + /// stored in \a output of size \a output_size (including NULL) /// - inline char *narrow(char *output,size_t output_size,wchar_t const *source) + /// If there is not enough room NULL is returned, else output is returned. + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// + inline wchar_t* widen(wchar_t* output, size_t output_size, const char* begin, const char* end) { - return basic_convert(output,output_size,source,details::basic_strend(source)); + return utf::convert_buffer(output, output_size, begin, end); } /// - /// Convert UTF text in range [begin,end) to NULL terminated \a output string of size at + /// Convert NULL terminated narrow string (UTF-8) to NULL terminated wide string (UTF-16/32) /// most output_size (including NULL) - /// - /// In case of success output is returned, if the input sequence is illegal, - /// or there is not enough room NULL is returned /// - inline char *narrow(char *output,size_t output_size,wchar_t const *begin,wchar_t const *end) + /// If there is not enough room NULL is returned, else output is returned. + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// + inline wchar_t* widen(wchar_t* output, size_t output_size, const char* source) { - return basic_convert(output,output_size,begin,end); + return widen(output, output_size, source, source + utf::strlen(source)); } + /// - /// Convert NULL terminated UTF source string to NULL terminated \a output string of size at - /// most output_size (including NULL) - /// - /// In case of success output is returned, if the input sequence is illegal, - /// or there is not enough room NULL is returned + /// Convert wide string (UTF-16/32) to narrow string (UTF-8). + /// + /// \param s Input string + /// \param count Number of characters to convert + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline wchar_t *widen(wchar_t *output,size_t output_size,char const *source) + template> + inline std::string narrow(const T_Char* s, size_t count) { - return basic_convert(output,output_size,source,details::basic_strend(source)); + return utf::convert_string(s, s + count); } /// - /// Convert UTF text in range [begin,end) to NULL terminated \a output string of size at - /// most output_size (including NULL) - /// - /// In case of success output is returned, if the input sequence is illegal, - /// or there is not enough room NULL is returned + /// Convert wide string (UTF-16/32) to narrow string (UTF-8). + /// + /// \param s NULL terminated input string + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline wchar_t *widen(wchar_t *output,size_t output_size,char const *begin,char const *end) + template> + inline std::string narrow(const T_Char* s) { - return basic_convert(output,output_size,begin,end); + return narrow(s, utf::strlen(s)); } - - /// - /// Convert between Wide - UTF-16/32 string and UTF-8 string. + /// Convert wide string (UTF-16/32) to narrow string (UTF-8). /// - /// boost::locale::conv::conversion_error is thrown in a case of a error + /// \param s Input string + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline std::string narrow(wchar_t const *s) + template> + inline std::string narrow(const StringOrStringView& s) { - return boost::locale::conv::utf_to_utf(s); + return utf::convert_string(s.data(), s.data() + s.size()); } + /// - /// Convert between UTF-8 and UTF-16 string, implemented only on Windows platform + /// Convert narrow string (UTF-8) to wide string (UTF-16/32). /// - /// boost::locale::conv::conversion_error is thrown in a case of a error + /// \param s Input string + /// \param count Number of characters to convert + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline std::wstring widen(char const *s) + template> + inline std::wstring widen(const T_Char* s, size_t count) { - return boost::locale::conv::utf_to_utf(s); + return utf::convert_string(s, s + count); } /// - /// Convert between Wide - UTF-16/32 string and UTF-8 string + /// Convert narrow string (UTF-8) to wide string (UTF-16/32). /// - /// boost::locale::conv::conversion_error is thrown in a case of a error + /// \param s NULL terminated input string + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline std::string narrow(std::wstring const &s) + template> + inline std::wstring widen(const T_Char* s) { - return boost::locale::conv::utf_to_utf(s); + return widen(s, utf::strlen(s)); } /// - /// Convert between UTF-8 and UTF-16 string, implemented only on Windows platform + /// Convert narrow string (UTF-8) to wide string (UTF-16/32). /// - /// boost::locale::conv::conversion_error is thrown in a case of a error + /// \param s Input string + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER /// - inline std::wstring widen(std::string const &s) + template> + inline std::wstring widen(const StringOrStringView& s) { - return boost::locale::conv::utf_to_utf(s); + return utf::convert_string(s.data(), s.data() + s.size()); } - -} // nowide +} // namespace nowide } // namespace boost #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/cstdio.hpp b/xs/src/boost/nowide/cstdio.hpp old mode 100755 new mode 100644 index d0bda97a01..f3b4b5dbec --- a/xs/src/boost/nowide/cstdio.hpp +++ b/xs/src/boost/nowide/cstdio.hpp @@ -1,25 +1,16 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_NOWIDE_CSTDIO_H_INCLUDED -#define BOOST_NOWIDE_CSTDIO_H_INCLUDED +#ifndef BOOST_NOWIDE_CSTDIO_HPP_INCLUDED +#define BOOST_NOWIDE_CSTDIO_HPP_INCLUDED +#include #include -#include -#include -#include -#include -#include - -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable : 4996) -#endif - namespace boost { namespace nowide { @@ -30,72 +21,27 @@ namespace nowide { using std::rename; #else -/// -/// \brief Same as freopen but file_name and mode are UTF-8 strings -/// -/// If invalid UTF-8 given, NULL is returned and errno is set to EINVAL -/// -inline FILE *freopen(char const *file_name,char const *mode,FILE *stream) -{ - wstackstring wname; - wshort_stackstring wmode; - if(!wname.convert(file_name) || !wmode.convert(mode)) { - errno = EINVAL; - return 0; - } - return _wfreopen(wname.c_str(),wmode.c_str(),stream); -} -/// -/// \brief Same as fopen but file_name and mode are UTF-8 strings -/// -/// If invalid UTF-8 given, NULL is returned and errno is set to EINVAL -/// -inline FILE *fopen(char const *file_name,char const *mode) -{ - wstackstring wname; - wshort_stackstring wmode; - if(!wname.convert(file_name) || !wmode.convert(mode)) { - errno = EINVAL; - return 0; - } - return _wfopen(wname.c_str(),wmode.c_str()); -} -/// -/// \brief Same as rename but old_name and new_name are UTF-8 strings -/// -/// If invalid UTF-8 given, -1 is returned and errno is set to EINVAL -/// -inline int rename(char const *old_name,char const *new_name) -{ - wstackstring wold,wnew; - if(!wold.convert(old_name) || !wnew.convert(new_name)) { - errno = EINVAL; - return -1; - } - return _wrename(wold.c_str(),wnew.c_str()); -} -/// -/// \brief Same as rename but name is UTF-8 string -/// -/// If invalid UTF-8 given, -1 is returned and errno is set to EINVAL -/// -inline int remove(char const *name) -{ - wstackstring wname; - if(!wname.convert(name)) { - errno = EINVAL; - return -1; - } - return _wremove(wname.c_str()); -} + /// + /// \brief Same as freopen but file_name and mode are UTF-8 strings + /// + BOOST_NOWIDE_DECL FILE* freopen(const char* file_name, const char* mode, FILE* stream); + /// + /// \brief Same as fopen but file_name and mode are UTF-8 strings + /// + BOOST_NOWIDE_DECL FILE* fopen(const char* file_name, const char* mode); + /// + /// \brief Same as rename but old_name and new_name are UTF-8 strings + /// + BOOST_NOWIDE_DECL int rename(const char* old_name, const char* new_name); + /// + /// \brief Same as rename but name is UTF-8 string + /// + BOOST_NOWIDE_DECL int remove(const char* name); #endif -} // nowide + namespace detail { + BOOST_NOWIDE_DECL FILE* wfopen(const wchar_t* filename, const wchar_t* mode); + } +} // namespace nowide } // namespace boost -#ifdef BOOST_MSVC -#pragma warning(pop) -#endif - #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/cstdlib.hpp b/xs/src/boost/nowide/cstdlib.hpp old mode 100755 new mode 100644 index 27e20610a3..1f89e72983 --- a/xs/src/boost/nowide/cstdlib.hpp +++ b/xs/src/boost/nowide/cstdlib.hpp @@ -2,15 +2,66 @@ // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED #define BOOST_NOWIDE_CSTDLIB_HPP_INCLUDED -#include -#include +#include +#if !defined(BOOST_WINDOWS) +#include +#endif + +namespace boost { +namespace nowide { +#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) + using std::getenv; + using std::system; +#else + /// + /// \brief UTF-8 aware getenv. Returns 0 if the variable is not set. + /// + /// This function is not thread safe or reenterable as defined by the standard library + /// + BOOST_NOWIDE_DECL char* getenv(const char* key); + + /// + /// Same as std::system but cmd is UTF-8. + /// + BOOST_NOWIDE_DECL int system(const char* cmd); + +#endif + /// + /// \brief Set environment variable \a key to \a value + /// + /// if overwrite is not 0, that the old value is always overwritten, otherwise, + /// if the variable exists it remains unchanged + /// + /// \a key and \a value are UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int setenv(const char* key, const char* value, int overwrite); + + /// + /// \brief Remove environment variable \a key + /// + /// \a key is UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int unsetenv(const char* key); + + /// + /// \brief Adds or changes an environment variable, \a string must be in format KEY=VALUE + /// + /// \a string MAY become part of the environment, hence changes to the value MAY change + /// the environment. For portability it is hence recommended NOT to change it. + /// \a string is UTF-8 on Windows + /// \return zero on success, else nonzero + /// + BOOST_NOWIDE_DECL int putenv(char* string); + +} // namespace nowide +} // namespace boost #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/detail/convert.hpp b/xs/src/boost/nowide/detail/convert.hpp new file mode 100644 index 0000000000..5fcf3a1a78 --- /dev/null +++ b/xs/src/boost/nowide/detail/convert.hpp @@ -0,0 +1,25 @@ +// +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_DETAIL_CONVERT_HPP_INCLUDED +#define BOOST_NOWIDE_DETAIL_CONVERT_HPP_INCLUDED + +#include + +// Legacy compatibility header only. Include instead + +namespace boost { +namespace nowide { + namespace detail { + using boost::nowide::utf::convert_buffer; + using boost::nowide::utf::convert_string; + using boost::nowide::utf::strlen; + } // namespace detail +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/detail/is_path.hpp b/xs/src/boost/nowide/detail/is_path.hpp new file mode 100644 index 0000000000..aee11b4394 --- /dev/null +++ b/xs/src/boost/nowide/detail/is_path.hpp @@ -0,0 +1,39 @@ +// +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_DETAIL_IS_PATH_HPP_INCLUDED +#define BOOST_NOWIDE_DETAIL_IS_PATH_HPP_INCLUDED + +#include + +namespace boost { +namespace nowide { + namespace detail { + + /// Trait to heuristically check for a *\::filesystem::path + /// Done by checking for make_preferred and filename member functions with correct signature + template + struct is_path + { + template + struct Check; + template + static std::true_type test(Check*); + template + static std::false_type test(...); + + static constexpr bool value = decltype(test(0))::value; + }; + /// SFINAE trait which has a member "type = Result" if the Path is a *\::filesystem::path + template + using enable_if_path_t = typename std::enable_if::value, Result>::type; + + } // namespace detail +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/detail/is_string_container.hpp b/xs/src/boost/nowide/detail/is_string_container.hpp new file mode 100644 index 0000000000..1b54a60868 --- /dev/null +++ b/xs/src/boost/nowide/detail/is_string_container.hpp @@ -0,0 +1,96 @@ +// +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED +#define BOOST_NOWIDE_DETAIL_IS_STRING_CONTAINER_HPP_INCLUDED + +#include +#include + +namespace boost { +namespace nowide { + namespace detail { + template + struct make_void + { + typedef void type; + }; + + template + using void_t = typename make_void::type; + + template + struct is_char_type : std::false_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; + template<> + struct is_char_type : std::true_type + {}; +#ifdef __cpp_char8_t + template<> + struct is_char_type : std::true_type + {}; +#endif + + template + struct is_c_string : std::false_type + {}; + template + struct is_c_string : is_char_type + {}; + + template + using const_data_result = decltype(std::declval().data()); + /// Return the size of the char type returned by the data() member function + template + using get_data_width = + std::integral_constant>::type)>; + template + using size_result = decltype(std::declval().size()); + /// Return true if the data() member function returns a pointer to a type of size 1 + template + using has_narrow_data = std::integral_constant::value == 1)>; + + /// Return true if T is a string container, e.g. std::basic_string, std::basic_string_view + /// Requires a static value `npos`, a member function `size()` returning an integral, + /// and a member function `data()` returning a C string + template + struct is_string_container : std::false_type + {}; + // clang-format off + template + struct is_string_container, const_data_result>> + : std::integral_constant::value + && std::is_integral>::value + && is_c_string>::value + && isNarrow == has_narrow_data::value> + {}; + // clang-format on + template + using requires_narrow_string_container = typename std::enable_if::value>::type; + template + using requires_wide_string_container = typename std::enable_if::value>::type; + + template + using requires_narrow_char = typename std::enable_if::value>::type; + template + using requires_wide_char = typename std::enable_if<(sizeof(T) > 1) && is_char_type::value>::type; + + } // namespace detail +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/detail/utf.hpp b/xs/src/boost/nowide/detail/utf.hpp new file mode 100644 index 0000000000..0930281765 --- /dev/null +++ b/xs/src/boost/nowide/detail/utf.hpp @@ -0,0 +1,23 @@ +// +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_DETAIL_UTF_HPP_INCLUDED +#define BOOST_NOWIDE_DETAIL_UTF_HPP_INCLUDED + +#include + +// Legacy compatibility header only. Include instead + +namespace boost { +namespace nowide { + namespace detail { + namespace utf = boost::nowide::utf; + } // namespace detail +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/filebuf.hpp b/xs/src/boost/nowide/filebuf.hpp old mode 100755 new mode 100644 index 2649782fd6..44c7a51362 --- a/xs/src/boost/nowide/filebuf.hpp +++ b/xs/src/boost/nowide/filebuf.hpp @@ -1,134 +1,190 @@ // // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2019-2020 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_NOWIDE_FILEBUF_HPP -#define BOOST_NOWIDE_FILEBUF_HPP +#ifndef BOOST_NOWIDE_FILEBUF_HPP_INCLUDED +#define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED -#include -#include +#include +#if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT +#include #include -#include +#include +#include +#include +#include +#include +#include #include -#include - -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable : 4996 4244 4800) +#else +#include #endif - namespace boost { namespace nowide { -#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_FSTREAM_TESTS) && !defined(BOOST_NOWIDE_DOXYGEN) + namespace detail { + /// Same as std::ftell but potentially with Large File Support + BOOST_NOWIDE_DECL std::streampos ftell(FILE* file); + /// Same as std::fseek but potentially with Large File Support + BOOST_NOWIDE_DECL int fseek(FILE* file, std::streamoff offset, int origin); + } // namespace detail + +#if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN) using std::basic_filebuf; using std::filebuf; #else // Windows - /// - /// \brief This forward declaration defined the basic_filebuf type. + /// \brief This forward declaration defines the basic_filebuf type. /// - /// it is implemented and specialized for CharType = char, it behaves + /// it is implemented and specialized for CharType = char, it /// implements std::filebuf over standard C I/O /// - template > + template> class basic_filebuf; - + /// - /// \brief This is implementation of std::filebuf + /// \brief This is the implementation of std::filebuf /// - /// it is implemented and specialized for CharType = char, it behaves + /// it is implemented and specialized for CharType = char, it /// implements std::filebuf over standard C I/O /// template<> - class basic_filebuf : public std::basic_streambuf { + class basic_filebuf : public std::basic_streambuf + { + using Traits = std::char_traits; + public: +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable : 4351) // new behavior : elements of array will be default initialized +#endif /// /// Creates new filebuf /// - basic_filebuf() : - buffer_size_(4), - buffer_(0), - file_(0), - own_(true), - mode_(std::ios::in | std::ios::out) + basic_filebuf() : + buffer_size_(BUFSIZ), buffer_(0), file_(0), owns_buffer_(false), last_char_(), + mode_(std::ios_base::openmode(0)) { - setg(0,0,0); - setp(0,0); + setg(0, 0, 0); + setp(0, 0); } - - virtual ~basic_filebuf() +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + basic_filebuf(const basic_filebuf&) = delete; + basic_filebuf& operator=(const basic_filebuf&) = delete; + basic_filebuf(basic_filebuf&& other) noexcept : basic_filebuf() { - if(file_) { - ::fclose(file_); - file_ = 0; + swap(other); + } + basic_filebuf& operator=(basic_filebuf&& other) noexcept + { + close(); + swap(other); + return *this; + } + void swap(basic_filebuf& rhs) + { + std::basic_streambuf::swap(rhs); + using std::swap; + swap(buffer_size_, rhs.buffer_size_); + swap(buffer_, rhs.buffer_); + swap(file_, rhs.file_); + swap(owns_buffer_, rhs.owns_buffer_); + swap(last_char_[0], rhs.last_char_[0]); + swap(mode_, rhs.mode_); + + // Fixup last_char references + if(pbase() == rhs.last_char_) + setp(last_char_, (pptr() == epptr()) ? last_char_ : last_char_ + 1); + if(eback() == rhs.last_char_) + setg(last_char_, (gptr() == rhs.last_char_) ? last_char_ : last_char_ + 1, last_char_ + 1); + + if(rhs.pbase() == last_char_) + rhs.setp(rhs.last_char_, (rhs.pptr() == rhs.epptr()) ? rhs.last_char_ : rhs.last_char_ + 1); + if(rhs.eback() == last_char_) + { + rhs.setg(rhs.last_char_, + (rhs.gptr() == last_char_) ? rhs.last_char_ : rhs.last_char_ + 1, + rhs.last_char_ + 1); } - if(own_ && buffer_) - delete [] buffer_; } - + + virtual ~basic_filebuf() + { + close(); + } + /// /// Same as std::filebuf::open but s is UTF-8 string /// - basic_filebuf *open(std::string const &s,std::ios_base::openmode mode) + basic_filebuf* open(const std::string& s, std::ios_base::openmode mode) { - return open(s.c_str(),mode); + return open(s.c_str(), mode); } /// /// Same as std::filebuf::open but s is UTF-8 string /// - basic_filebuf *open(char const *s,std::ios_base::openmode mode) + basic_filebuf* open(const char* s, std::ios_base::openmode mode) { - if(file_) { - sync(); - ::fclose(file_); - file_ = 0; - } - bool ate = bool(mode & std::ios_base::ate); + const wstackstring name(s); + return open(name.get(), mode); + } + /// Opens the file with the given name, see std::filebuf::open + basic_filebuf* open(const wchar_t* s, std::ios_base::openmode mode) + { + if(is_open()) + return NULL; + validate_cvt(this->getloc()); + const bool ate = (mode & std::ios_base::ate) != 0; if(ate) - mode = mode ^ std::ios_base::ate; - wchar_t const *smode = get_mode(mode); + mode &= ~std::ios_base::ate; + const wchar_t* smode = get_mode(mode); if(!smode) return 0; - wstackstring name; - if(!name.convert(s)) - return 0; - #ifdef BOOST_NOWIDE_FSTREAM_TESTS - FILE *f = ::fopen(s,boost::nowide::convert(smode).c_str()); - #else - FILE *f = ::_wfopen(name.c_str(),smode); - #endif - if(!f) + file_ = detail::wfopen(s, smode); + if(!file_) return 0; - if(ate && fseek(f,0,SEEK_END)!=0) { - fclose(f); + if(ate && detail::fseek(file_, 0, SEEK_END) != 0) + { + close(); return 0; } - file_ = f; + mode_ = mode; return this; } /// /// Same as std::filebuf::close() /// - basic_filebuf *close() + basic_filebuf* close() { + if(!is_open()) + return NULL; bool res = sync() == 0; - if(file_) { - if(::fclose(file_)!=0) - res = false; - file_ = 0; + if(std::fclose(file_) != 0) + res = false; + file_ = NULL; + mode_ = std::ios_base::openmode(0); + if(owns_buffer_) + { + delete[] buffer_; + buffer_ = NULL; + owns_buffer_ = false; } - return res ? this : 0; + setg(0, 0, 0); + setp(0, 0); + return res ? this : NULL; } /// /// Same as std::filebuf::is_open() /// bool is_open() const { - return file_ != 0; + return file_ != NULL; } private: @@ -136,211 +192,230 @@ namespace nowide { { if(buffer_) return; - if(buffer_size_ > 0) { - buffer_ = new char [buffer_size_]; - own_ = true; + if(buffer_size_ > 0) + { + buffer_ = new char[buffer_size_]; + owns_buffer_ = true; } } - protected: - - virtual std::streambuf *setbuf(char *s,std::streamsize n) + void validate_cvt(const std::locale& loc) { - if(!buffer_ && n>=0) { - buffer_ = s; - buffer_size_ = n; - own_ = false; - } - return this; + if(!std::use_facet>(loc).always_noconv()) + throw std::runtime_error("Converting codecvts are not supported"); } - -#ifdef BOOST_NOWIDE_DEBUG_FILEBUF - void print_buf(char *b,char *p,char *e) - { - std::cerr << "-- Is Null: " << (b==0) << std::endl;; - if(b==0) - return; - if(e != 0) - std::cerr << "-- Total: " << e - b <<" offset from start " << p - b << std::endl; - else - std::cerr << "-- Total: " << p - b << std::endl; - - std::cerr << "-- ["; - for(char *ptr = b;ptrprint_state(); - } - ~print_guard() + assert(n >= 0); + // Maximum compatibility: Discard all local buffers and use user-provided values + // Users should call sync() before or better use it before any IO is done or any file is opened + setg(NULL, NULL, NULL); + setp(NULL, NULL); + if(owns_buffer_) { - std::cerr << "Out: " << f << std::endl; - self->print_state(); + delete[] buffer_; + owns_buffer_ = false; } - basic_filebuf *self; - char const *f; - }; -#else -#endif - - int overflow(int c) + buffer_ = s; + buffer_size_ = (n >= 0) ? static_cast(n) : 0; + return this; + } + + int overflow(int c = EOF) override { -#ifdef BOOST_NOWIDE_DEBUG_FILEBUF - print_guard g(this,__FUNCTION__); -#endif - if(!file_) + if(!(mode_ & (std::ios_base::out | std::ios_base::app))) return EOF; - - if(fixg() < 0) + + if(!stop_reading()) return EOF; size_t n = pptr() - pbase(); - if(n > 0) { - if(::fwrite(pbase(),1,n,file_) < n) - return -1; - fflush(file_); - } - - if(buffer_size_ > 0) { - make_buffer(); - setp(buffer_,buffer_+buffer_size_); - if(c!=EOF) - sputc(c); - } - else if(c!=EOF) { - if(::fputc(c,file_)==EOF) + if(n > 0) + { + if(std::fwrite(pbase(), 1, n, file_) != n) + return EOF; + setp(buffer_, buffer_ + buffer_size_); + if(c != EOF) + { + *buffer_ = Traits::to_char_type(c); + pbump(1); + } + } else if(c != EOF) + { + if(buffer_size_ > 0) + { + make_buffer(); + setp(buffer_, buffer_ + buffer_size_); + *buffer_ = Traits::to_char_type(c); + pbump(1); + } else if(std::fputc(c, file_) == EOF) + { return EOF; - fflush(file_); + } else if(!pptr()) + { + // Set to dummy value so we know we have written something + setp(last_char_, last_char_); + } } - return 0; + return Traits::not_eof(c); } - - - int sync() + + int sync() override { - return overflow(EOF); + if(!file_) + return 0; + bool result; + if(pptr()) + { + result = overflow() != EOF; + // Only flush if anything was written, otherwise behavior of fflush is undefined + if(std::fflush(file_) != 0) + return result = false; + } else + result = stop_reading(); + return result ? 0 : -1; } - int underflow() + int underflow() override { -#ifdef BOOST_NOWIDE_DEBUG_FILEBUF - print_guard g(this,__FUNCTION__); -#endif - if(!file_) + if(!(mode_ & std::ios_base::in)) return EOF; - if(fixp() < 0) + if(!stop_writing()) return EOF; - if(buffer_size_ == 0) { - int c = ::fgetc(file_); - if(c==EOF) { + // In text mode we cannot use a buffer size of more than 1 (i.e. single char only) + // This is due to the need to seek back in case of a sync to "put back" unread chars. + // However determining the number of chars to seek back is impossible in case there are newlines + // as we cannot know if those were converted. + if(buffer_size_ == 0 || !(mode_ & std::ios_base::binary)) + { + const int c = std::fgetc(file_); + if(c == EOF) + return EOF; + last_char_[0] = Traits::to_char_type(c); + setg(last_char_, last_char_, last_char_ + 1); + } else + { + make_buffer(); + const size_t n = std::fread(buffer_, 1, buffer_size_, file_); + setg(buffer_, buffer_, buffer_ + n); + if(n == 0) return EOF; - } - last_char_ = c; - setg(&last_char_,&last_char_,&last_char_ + 1); - return c; } - make_buffer(); - size_t n = ::fread(buffer_,1,buffer_size_,file_); - setg(buffer_,buffer_,buffer_+n); - if(n == 0) - return EOF; - return std::char_traits::to_int_type(*gptr()); + return Traits::to_int_type(*gptr()); } - int pbackfail(int) + int pbackfail(int c = EOF) override { - return pubseekoff(-1,std::ios::cur); + if(!(mode_ & std::ios_base::in)) + return EOF; + if(!stop_writing()) + return EOF; + if(gptr() > eback()) + gbump(-1); + else if(seekoff(-1, std::ios_base::cur) != std::streampos(std::streamoff(-1))) + { + if(underflow() == EOF) + return EOF; + } else + return EOF; + + // Case 1: Caller just wanted space for 1 char + if(c == EOF) + return Traits::not_eof(c); + // Case 2: Caller wants to put back different char + // gptr now points to the (potentially newly read) previous char + if(*gptr() != c) + *gptr() = Traits::to_char_type(c); + return Traits::not_eof(c); } std::streampos seekoff(std::streamoff off, - std::ios_base::seekdir seekdir, - std::ios_base::openmode /*m*/) + std::ios_base::seekdir seekdir, + std::ios_base::openmode = std::ios_base::in | std::ios_base::out) override { -#ifdef BOOST_NOWIDE_DEBUG_FILEBUF - print_guard g(this,__FUNCTION__); -#endif if(!file_) return EOF; - if(fixp() < 0 || fixg() < 0) + // Switching between input<->output requires a seek + // So do NOT optimize for seekoff(0, cur) as No-OP + + // On some implementations a seek also flushes, so do a full sync + if(sync() != 0) return EOF; - if(seekdir == std::ios_base::cur) { - if( ::fseek(file_,off,SEEK_CUR) < 0) - return EOF; - } - else if(seekdir == std::ios_base::beg) { - if( ::fseek(file_,off,SEEK_SET) < 0) - return EOF; - } - else if(seekdir == std::ios_base::end) { - if( ::fseek(file_,off,SEEK_END) < 0) - return EOF; + int whence; + switch(seekdir) + { + case std::ios_base::beg: whence = SEEK_SET; break; + case std::ios_base::cur: whence = SEEK_CUR; break; + case std::ios_base::end: whence = SEEK_END; break; + default: assert(false); return EOF; } - else - return -1; - return ftell(file_); + if(detail::fseek(file_, off, whence) != 0) + return EOF; + return detail::ftell(file_); } - std::streampos seekpos(std::streampos off,std::ios_base::openmode m) + std::streampos seekpos(std::streampos pos, + std::ios_base::openmode m = std::ios_base::in | std::ios_base::out) override { - return seekoff(std::streamoff(off),std::ios_base::beg,m); + // Standard mandates "as-if fsetpos", but assume the effect is the same as fseek + return seekoff(pos, std::ios_base::beg, m); } + void imbue(const std::locale& loc) override + { + validate_cvt(loc); + } + private: - int fixg() + /// Stop reading adjusting the file pointer if necessary + /// Postcondition: gptr() == NULL + bool stop_reading() { - if(gptr()!=egptr()) { - std::streamsize off = gptr() - egptr(); - setg(0,0,0); - if(fseek(file_,off,SEEK_CUR) != 0) - return -1; - } - setg(0,0,0); - return 0; + if(!gptr()) + return true; + const auto off = gptr() - egptr(); + setg(0, 0, 0); + if(!off) + return true; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + // coverity[result_independent_of_operands] + if(off > std::numeric_limits::max()) + return false; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + return detail::fseek(file_, static_cast(off), SEEK_CUR) == 0; } - - int fixp() + + /// Stop writing. If any bytes are to be written, writes them to file + /// Postcondition: pptr() == NULL + bool stop_writing() { - if(pptr()!=0) { - int r = sync(); - setp(0,0); - return r; + if(pptr()) + { + const char* const base = pbase(); + const size_t n = pptr() - base; + setp(0, 0); + if(n && std::fwrite(base, 1, n, file_) != n) + return false; } - return 0; + return true; } - void reset(FILE *f = 0) + void reset(FILE* f = 0) { sync(); - if(file_) { + if(file_) + { fclose(file_); file_ = 0; } file_ = f; } - - - static wchar_t const *get_mode(std::ios_base::openmode mode) + + static const wchar_t* get_mode(std::ios_base::openmode mode) { // // done according to n2914 table 106 27.9.1.4 @@ -384,32 +459,32 @@ namespace nowide { return L"a+b"; if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app)) return L"a+b"; - return 0; + return 0; } - + size_t buffer_size_; - char *buffer_; - FILE *file_; - bool own_; - char last_char_; + char* buffer_; + FILE* file_; + bool owns_buffer_; + char last_char_[1]; std::ios::openmode mode_; }; - + /// /// \brief Convenience typedef /// - typedef basic_filebuf filebuf; - - #endif // windows - -} // nowide -} // namespace boost + using filebuf = basic_filebuf; -#ifdef BOOST_MSVC -# pragma warning(pop) -#endif + /// Swap the basic_filebuf instances + template + void swap(basic_filebuf& lhs, basic_filebuf& rhs) + { + lhs.swap(rhs); + } +#endif // windows -#endif +} // namespace nowide +} // namespace boost -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 +#endif diff --git a/xs/src/boost/nowide/filesystem.hpp b/xs/src/boost/nowide/filesystem.hpp new file mode 100644 index 0000000000..68e398d798 --- /dev/null +++ b/xs/src/boost/nowide/filesystem.hpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2012 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED +#define BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED + +#include +#include + +namespace boost { +namespace nowide { + /// + /// Install utf8_codecvt facet into boost::filesystem::path such all char strings are interpreted as utf-8 strings + /// + inline std::locale nowide_filesystem() + { + std::locale tmp = std::locale(std::locale(), new boost::nowide::utf8_codecvt()); + return boost::filesystem::path::imbue(tmp); + } +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/fstream.hpp b/xs/src/boost/nowide/fstream.hpp old mode 100755 new mode 100644 index 35604d277b..ca946f310c --- a/xs/src/boost/nowide/fstream.hpp +++ b/xs/src/boost/nowide/fstream.hpp @@ -2,282 +2,357 @@ // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_NOWIDE_FSTREAM_INCLUDED_HPP -#define BOOST_NOWIDE_FSTREAM_INCLUDED_HPP +#ifndef BOOST_NOWIDE_FSTREAM_HPP_INCLUDED +#define BOOST_NOWIDE_FSTREAM_HPP_INCLUDED -//#include -#include -#include -#include -#include -#include +#include +#include #include +#include +#include +#include namespace boost { -/// -/// \brief This namespace includes implementation of the standard library functions -/// such that they accept UTF-8 strings on Windows. On other platforms it is just an alias -/// of std namespace (i.e. not on Windows) -/// namespace nowide { -#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_FSTREAM_TESTS) && !defined(BOOST_NOWIDE_DOXYGEN) + /// \cond INTERNAL + namespace detail { + // clang-format off + struct StreamTypeIn + { + static std::ios_base::openmode mode() { return std::ios_base::in; } + static std::ios_base::openmode mode_modifier() { return mode(); } + template + struct stream_base{ + using type = std::basic_istream; + }; + }; + struct StreamTypeOut + { + static std::ios_base::openmode mode() { return std::ios_base::out; } + static std::ios_base::openmode mode_modifier() { return mode(); } + template + struct stream_base{ + using type = std::basic_ostream; + }; + }; + struct StreamTypeInOut + { + static std::ios_base::openmode mode() { return std::ios_base::in | std::ios_base::out; } + static std::ios_base::openmode mode_modifier() { return std::ios_base::openmode(); } + template + struct stream_base{ + using type = std::basic_iostream; + }; + }; + // clang-format on - using std::basic_ifstream; - using std::basic_ofstream; - using std::basic_fstream; - using std::ifstream; - using std::ofstream; - using std::fstream; + /// Base class for all basic_*fstream classes + /// Contains basic_filebuf instance so its pointer can be used to construct basic_*stream + /// Provides common functions to reduce boilerplate code including inheriting from + /// the correct std::basic_[io]stream class and initializing it + /// \tparam T_StreamType One of StreamType* above. + /// Class used instead of value, because openmode::operator| may not be constexpr + /// \tparam FileBufType Discriminator to force a differing ABI if depending on the contained filebuf + template + class fstream_impl; + + } // namespace detail + /// \endcond -#else /// /// \brief Same as std::basic_ifstream but accepts UTF-8 strings under Windows /// - template > - class basic_ifstream : public std::basic_istream + template> + class basic_ifstream : public detail::fstream_impl { + using fstream_impl = detail::fstream_impl; + public: - typedef basic_filebuf internal_buffer_type; - typedef std::basic_istream internal_stream_type; + basic_ifstream() + {} - basic_ifstream() : - internal_stream_type(0) + explicit basic_ifstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::in) { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); + open(file_name, mode); } - - explicit basic_ifstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::in) : - internal_stream_type(0) +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS + explicit basic_ifstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::in) { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); - } - - explicit basic_ifstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::in) : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); + open(file_name, mode); } +#endif - - void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::in) - { - open(file_name.c_str(),mode); - } - void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::in) - { - if(!buf_->open(file_name,mode | std::ios_base::in)) { - this->setstate(std::ios_base::failbit); - } - else { - this->clear(); - } - } - bool is_open() - { - return buf_->is_open(); - } - bool is_open() const - { - return buf_->is_open(); - } - void close() + explicit basic_ifstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::in) { - if(!buf_->close()) - this->setstate(std::ios_base::failbit); - else - this->clear(); + open(file_name, mode); } - internal_buffer_type *rdbuf() const + template + explicit basic_ifstream(const Path& file_name, + detail::enable_if_path_t mode = std::ios_base::in) { - return buf_.get(); + open(file_name, mode); } - ~basic_ifstream() + using fstream_impl::open; + using fstream_impl::is_open; + using fstream_impl::close; + using fstream_impl::rdbuf; + using fstream_impl::swap; + basic_ifstream(const basic_ifstream&) = delete; + basic_ifstream& operator=(const basic_ifstream&) = delete; + basic_ifstream(basic_ifstream&& other) noexcept : fstream_impl(std::move(other)) + {} + basic_ifstream& operator=(basic_ifstream&& rhs) noexcept { - buf_->close(); + fstream_impl::operator=(std::move(rhs)); + return *this; } - - private: - boost::scoped_ptr buf_; }; /// /// \brief Same as std::basic_ofstream but accepts UTF-8 strings under Windows /// - template > - class basic_ofstream : public std::basic_ostream + template> + class basic_ofstream : public detail::fstream_impl { - public: - typedef basic_filebuf internal_buffer_type; - typedef std::basic_ostream internal_stream_type; + using fstream_impl = detail::fstream_impl; - basic_ofstream() : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - } - explicit basic_ofstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::out) : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); - } - explicit basic_ofstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out) : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); - } - void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out) + public: + basic_ofstream() + {} + explicit basic_ofstream(const char* file_name, std::ios_base::openmode mode = std::ios_base::out) { - open(file_name.c_str(),mode); + open(file_name, mode); } - void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::out) +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS + explicit basic_ofstream(const wchar_t* file_name, std::ios_base::openmode mode = std::ios_base::out) { - if(!buf_->open(file_name,mode | std::ios_base::out)) { - this->setstate(std::ios_base::failbit); - } - else { - this->clear(); - } + open(file_name, mode); } - bool is_open() - { - return buf_->is_open(); - } - bool is_open() const +#endif + explicit basic_ofstream(const std::string& file_name, std::ios_base::openmode mode = std::ios_base::out) { - return buf_->is_open(); + open(file_name, mode); } - void close() + template + explicit basic_ofstream(const Path& file_name, + detail::enable_if_path_t mode = std::ios_base::out) { - if(!buf_->close()) - this->setstate(std::ios_base::failbit); - else - this->clear(); + open(file_name, mode); } - internal_buffer_type *rdbuf() const - { - return buf_.get(); - } - ~basic_ofstream() + using fstream_impl::open; + using fstream_impl::is_open; + using fstream_impl::close; + using fstream_impl::rdbuf; + using fstream_impl::swap; + basic_ofstream(const basic_ofstream&) = delete; + basic_ofstream& operator=(const basic_ofstream&) = delete; + basic_ofstream(basic_ofstream&& other) noexcept : fstream_impl(std::move(other)) + {} + basic_ofstream& operator=(basic_ofstream&& rhs) { - buf_->close(); + fstream_impl::operator=(std::move(rhs)); + return *this; } - - private: - boost::scoped_ptr buf_; }; +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable : 4250) // : inherits via dominance +#endif /// /// \brief Same as std::basic_fstream but accepts UTF-8 strings under Windows /// - - template > - class basic_fstream : public std::basic_iostream + template> + class basic_fstream : public detail::fstream_impl { - public: - typedef basic_filebuf internal_buffer_type; - typedef std::basic_iostream internal_stream_type; + using fstream_impl = detail::fstream_impl; - basic_fstream() : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - } - explicit basic_fstream(char const *file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::in) : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); - } - explicit basic_fstream(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::in) : - internal_stream_type(0) - { - buf_.reset(new internal_buffer_type()); - std::ios::rdbuf(buf_.get()); - open(file_name,mode); - } - void open(std::string const &file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::out) - { - open(file_name.c_str(),mode); - } - void open(char const *file_name,std::ios_base::openmode mode = std::ios_base::out | std::ios_base::out) + public: + basic_fstream() + {} + explicit basic_fstream(const char* file_name, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { - if(!buf_->open(file_name,mode)) { - this->setstate(std::ios_base::failbit); - } - else { - this->clear(); - } + open(file_name, mode); } - bool is_open() +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS + explicit basic_fstream(const wchar_t* file_name, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { - return buf_->is_open(); + open(file_name, mode); } - bool is_open() const +#endif + explicit basic_fstream(const std::string& file_name, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { - return buf_->is_open(); + open(file_name, mode); } - void close() + template + explicit basic_fstream(const Path& file_name, + detail::enable_if_path_t mode = std::ios_base::in + | std::ios_base::out) { - if(!buf_->close()) - this->setstate(std::ios_base::failbit); - else - this->clear(); + open(file_name, mode); } - internal_buffer_type *rdbuf() const - { - return buf_.get(); - } - ~basic_fstream() + using fstream_impl::open; + using fstream_impl::is_open; + using fstream_impl::close; + using fstream_impl::rdbuf; + using fstream_impl::swap; + basic_fstream(const basic_fstream&) = delete; + basic_fstream& operator=(const basic_fstream&) = delete; + basic_fstream(basic_fstream&& other) noexcept : fstream_impl(std::move(other)) + {} + basic_fstream& operator=(basic_fstream&& rhs) { - buf_->close(); + fstream_impl::operator=(std::move(rhs)); + return *this; } - - private: - boost::scoped_ptr buf_; }; + template + void swap(basic_ifstream& lhs, basic_ifstream& rhs) + { + lhs.swap(rhs); + } + template + void swap(basic_ofstream& lhs, basic_ofstream& rhs) + { + lhs.swap(rhs); + } + template + void swap(basic_fstream& lhs, basic_fstream& rhs) + { + lhs.swap(rhs); + } /// - /// \brief Same as std::filebuf but accepts UTF-8 strings under Windows + /// Same as std::filebuf but accepts UTF-8 strings under Windows /// - typedef basic_filebuf filebuf; + using filebuf = basic_filebuf; /// /// Same as std::ifstream but accepts UTF-8 strings under Windows + /// and *\::filesystem::path on all systems /// - typedef basic_ifstream ifstream; + using ifstream = basic_ifstream; /// /// Same as std::ofstream but accepts UTF-8 strings under Windows + /// and *\::filesystem::path on all systems /// - typedef basic_ofstream ofstream; + using ofstream = basic_ofstream; /// /// Same as std::fstream but accepts UTF-8 strings under Windows + /// and *\::filesystem::path on all systems /// - typedef basic_fstream fstream; + using fstream = basic_fstream; -#endif -} // nowide -} // namespace boost + // Implementation + namespace detail { + /// Holds an instance of T + /// Required to make sure this is constructed first before passing it to sibling classes + template + struct buf_holder + { + T buf_; + }; + template + class fstream_impl : private buf_holder>, // must be first due to init order + public T_StreamType::template stream_base::type + { + using internal_buffer_type = basic_filebuf; + using base_buf_holder = buf_holder; + using stream_base = typename T_StreamType::template stream_base::type; + + public: + using stream_base::setstate; + using stream_base::clear; + + protected: + using base_buf_holder::buf_; + fstream_impl() : stream_base(&buf_) + {} + fstream_impl(const fstream_impl&) = delete; + fstream_impl& operator=(const fstream_impl&) = delete; + + // coverity[exn_spec_violation] + fstream_impl(fstream_impl&& other) noexcept : + base_buf_holder(std::move(other)), stream_base(std::move(other)) + { + this->set_rdbuf(rdbuf()); + } + fstream_impl& operator=(fstream_impl&& rhs) noexcept + { + base_buf_holder::operator=(std::move(rhs)); + stream_base::operator=(std::move(rhs)); + return *this; + } + void swap(fstream_impl& other) + { + stream_base::swap(other); + rdbuf()->swap(*other.rdbuf()); + } + void open(const std::string& file_name, std::ios_base::openmode mode = T_StreamType::mode()) + { + open(file_name.c_str(), mode); + } + template + detail::enable_if_path_t open(const Path& file_name, + std::ios_base::openmode mode = T_StreamType::mode()) + { + open(file_name.c_str(), mode); + } + void open(const char* file_name, std::ios_base::openmode mode = T_StreamType::mode()) + { + if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) + setstate(std::ios_base::failbit); + else + clear(); + } +#if BOOST_NOWIDE_USE_WCHAR_OVERLOADS + void open(const wchar_t* file_name, std::ios_base::openmode mode = T_StreamType::mode()) + { + if(!rdbuf()->open(file_name, mode | T_StreamType::mode_modifier())) + setstate(std::ios_base::failbit); + else + clear(); + } +#endif + bool is_open() + { + return rdbuf()->is_open(); + } + bool is_open() const + { + return rdbuf()->is_open(); + } + void close() + { + if(!rdbuf()->close()) + setstate(std::ios_base::failbit); + } + + internal_buffer_type* rdbuf() const + { + return const_cast(&buf_); + } + }; +#ifdef BOOST_MSVC +#pragma warning(pop) +#endif + } // namespace detail +} // namespace nowide +} // namespace boost #endif -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/integration/filesystem.hpp b/xs/src/boost/nowide/integration/filesystem.hpp deleted file mode 100755 index 91e17c7fb1..0000000000 --- a/xs/src/boost/nowide/integration/filesystem.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2012 Artyom Beilis (Tonkikh) -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED -#define BOOST_NOWIDE_INTEGRATION_FILESYSTEM_HPP_INCLUDED - -#include -#include -namespace boost { - namespace nowide { - /// - /// Install utf8_codecvt facet into boost::filesystem::path such all char strings are interpreted as utf-8 strings - /// - inline void nowide_filesystem() - { - std::locale tmp = std::locale(std::locale(),new boost::nowide::utf8_codecvt()); - boost::filesystem::path::imbue(tmp); - } - } // nowide -} // boost - -#endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/iostream.cpp b/xs/src/boost/nowide/iostream.cpp deleted file mode 100755 index 6b9099110b..0000000000 --- a/xs/src/boost/nowide/iostream.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// -// Copyright (c) 2012 Artyom Beilis (Tonkikh) -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -#define BOOST_NOWIDE_SOURCE -#include -#include -#include -#include - -#ifdef BOOST_WINDOWS - -#ifndef NOMINMAX -# define NOMINMAX -#endif - - -#include - -namespace boost { -namespace nowide { -namespace details { - class console_output_buffer : public std::streambuf { - public: - console_output_buffer(HANDLE h) : - handle_(h), - isatty_(false) - { - if(handle_) { - DWORD dummy; - isatty_ = GetConsoleMode(handle_,&dummy) == TRUE; - } - } - protected: - int sync() - { - return overflow(EOF); - } - int overflow(int c) - { - if(!handle_) - return -1; - int n = pptr() - pbase(); - int r = 0; - - if(n > 0 && (r=write(pbase(),n)) < 0) - return -1; - if(r < n) { - memmove(pbase(),pbase() + r,n-r); - } - setp(buffer_, buffer_ + buffer_size); - pbump(n-r); - if(c!=EOF) - sputc(c); - return 0; - } - private: - - int write(char const *p,int n) - { - namespace uf = boost::locale::utf; - char const *b = p; - char const *e = p+n; - DWORD size=0; - if(!isatty_) { - if(!WriteFile(handle_,p,n,&size,0) || static_cast(size) != n) - return -1; - return n; - } - if(n > buffer_size) - return -1; - wchar_t *out = wbuffer_; - uf::code_point c; - size_t decoded = 0; - while(p < e && (c = uf::utf_traits::decode(p,e))!=uf::illegal && c!=uf::incomplete) { - out = uf::utf_traits::encode(c,out); - decoded = p-b; - } - if(c==uf::illegal) - return -1; - if(!WriteConsoleW(handle_,wbuffer_,out - wbuffer_,&size,0)) - return -1; - return decoded; - } - - static const int buffer_size = 1024; - char buffer_[buffer_size]; - wchar_t wbuffer_[buffer_size]; // for null - HANDLE handle_; - bool isatty_; - }; - - class console_input_buffer: public std::streambuf { - public: - console_input_buffer(HANDLE h) : - handle_(h), - isatty_(false), - wsize_(0) - { - if(handle_) { - DWORD dummy; - isatty_ = GetConsoleMode(handle_,&dummy) == TRUE; - } - } - - protected: - int pbackfail(int c) - { - if(c==EOF) - return EOF; - - if(gptr()!=eback()) { - gbump(-1); - *gptr() = c; - return 0; - } - - if(pback_buffer_.empty()) { - pback_buffer_.resize(4); - char *b = &pback_buffer_[0]; - char *e = b + pback_buffer_.size(); - setg(b,e-1,e); - *gptr() = c; - } - else { - size_t n = pback_buffer_.size(); - std::vector tmp; - tmp.resize(n*2); - memcpy(&tmp[n],&pback_buffer_[0],n); - tmp.swap(pback_buffer_); - char *b = &pback_buffer_[0]; - char *e = b + n * 2; - char *p = b+n-1; - *p = c; - setg(b,p,e); - } - - return 0; - } - - int underflow() - { - if(!handle_) - return -1; - if(!pback_buffer_.empty()) - pback_buffer_.clear(); - - size_t n = read(); - setg(buffer_,buffer_,buffer_+n); - if(n == 0) - return EOF; - return std::char_traits::to_int_type(*gptr()); - } - - private: - - size_t read() - { - namespace uf = boost::locale::utf; - if(!isatty_) { - DWORD read_bytes = 0; - if(!ReadFile(handle_,buffer_,buffer_size,&read_bytes,0)) - return 0; - return read_bytes; - } - DWORD read_wchars = 0; - size_t n = wbuffer_size - wsize_; - if(!ReadConsoleW(handle_,wbuffer_,n,&read_wchars,0)) - return 0; - wsize_ += read_wchars; - char *out = buffer_; - wchar_t *b = wbuffer_; - wchar_t *e = b + wsize_; - wchar_t *p = b; - uf::code_point c; - wsize_ = e-p; - while(p < e && (c = uf::utf_traits::decode(p,e))!=uf::illegal && c!=uf::incomplete) { - out = uf::utf_traits::encode(c,out); - wsize_ = e-p; - } - - if(c==uf::illegal) - return -1; - - - if(c==uf::incomplete) { - memmove(b,e-wsize_,sizeof(wchar_t)*wsize_); - } - - return out - buffer_; - } - - static const size_t buffer_size = 1024 * 3; - static const size_t wbuffer_size = 1024; - char buffer_[buffer_size]; - wchar_t wbuffer_[buffer_size]; // for null - HANDLE handle_; - bool isatty_; - int wsize_; - std::vector pback_buffer_; - }; - - winconsole_ostream::winconsole_ostream(int fd) : std::ostream(0) - { - HANDLE h = 0; - switch(fd) { - case 1: - h = GetStdHandle(STD_OUTPUT_HANDLE); - break; - case 2: - h = GetStdHandle(STD_ERROR_HANDLE); - break; - } - d.reset(new console_output_buffer(h)); - std::ostream::rdbuf(d.get()); - } - - winconsole_ostream::~winconsole_ostream() - { - } - - winconsole_istream::winconsole_istream() : std::istream(0) - { - HANDLE h = GetStdHandle(STD_INPUT_HANDLE); - d.reset(new console_input_buffer(h)); - std::istream::rdbuf(d.get()); - } - - winconsole_istream::~winconsole_istream() - { - } - -} // details - -BOOST_NOWIDE_DECL details::winconsole_istream cin; -BOOST_NOWIDE_DECL details::winconsole_ostream cout(1); -BOOST_NOWIDE_DECL details::winconsole_ostream cerr(2); -BOOST_NOWIDE_DECL details::winconsole_ostream clog(2); - -namespace { - struct initialize { - initialize() - { - boost::nowide::cin.tie(&boost::nowide::cout); - boost::nowide::cerr.tie(&boost::nowide::cout); - boost::nowide::clog.tie(&boost::nowide::cout); - } - } inst; -} - - - -} // nowide -} // namespace boost - -#endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/iostream.hpp b/xs/src/boost/nowide/iostream.hpp old mode 100755 new mode 100644 index 6ab004a254..5e730a08d8 --- a/xs/src/boost/nowide/iostream.hpp +++ b/xs/src/boost/nowide/iostream.hpp @@ -1,99 +1,106 @@ -// // Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020-2021 Alexander Grund // -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) -// + #ifndef BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED #define BOOST_NOWIDE_IOSTREAM_HPP_INCLUDED #include -#include -#include -#include +#ifdef BOOST_WINDOWS #include +#include +#include -#ifdef BOOST_MSVC -# pragma warning(push) -# pragma warning(disable : 4251) +#include // must be the last #include +#else +#include #endif +#ifdef BOOST_MSVC +#pragma warning(push) +#pragma warning(disable : 4251) +#endif namespace boost { namespace nowide { - #if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) +#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) using std::cout; using std::cerr; using std::cin; using std::clog; - #else - - /// \cond INTERNAL - namespace details { +#else + + /// \cond INTERNAL + namespace detail { class console_output_buffer; class console_input_buffer; - - class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream { - winconsole_ostream(winconsole_ostream const &); - void operator=(winconsole_ostream const &); + + class BOOST_NOWIDE_DECL winconsole_ostream : public std::ostream + { public: - winconsole_ostream(int fd); + winconsole_ostream(int fd, winconsole_ostream* tieStream); ~winconsole_ostream(); + private: - boost::scoped_ptr d; + std::unique_ptr d; + // Ensure the std streams are initialized and alive during the lifetime of this instance + std::ios_base::Init init_; }; - class BOOST_NOWIDE_DECL winconsole_istream : public std::istream { - winconsole_istream(winconsole_istream const &); - void operator=(winconsole_istream const &); + class BOOST_NOWIDE_DECL winconsole_istream : public std::istream + { public: - - winconsole_istream(); + explicit winconsole_istream(winconsole_ostream* tieStream); ~winconsole_istream(); + private: - struct data; - boost::scoped_ptr d; + std::unique_ptr d; + // Ensure the std streams are initialized and alive during the lifetime of this instance + std::ios_base::Init init_; }; - } // details - + } // namespace detail + /// \endcond /// /// \brief Same as std::cin, but uses UTF-8 /// /// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio - /// - extern BOOST_NOWIDE_DECL details::winconsole_istream cin; + /// + extern BOOST_NOWIDE_DECL detail::winconsole_istream cin; /// /// \brief Same as std::cout, but uses UTF-8 /// /// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio - /// - extern BOOST_NOWIDE_DECL details::winconsole_ostream cout; + /// + extern BOOST_NOWIDE_DECL detail::winconsole_ostream cout; /// /// \brief Same as std::cerr, but uses UTF-8 /// /// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio - /// - extern BOOST_NOWIDE_DECL details::winconsole_ostream cerr; + /// + extern BOOST_NOWIDE_DECL detail::winconsole_ostream cerr; /// /// \brief Same as std::clog, but uses UTF-8 /// /// Note, the stream is not synchronized with stdio and not affected by std::ios::sync_with_stdio - /// - extern BOOST_NOWIDE_DECL details::winconsole_ostream clog; + /// + extern BOOST_NOWIDE_DECL detail::winconsole_ostream clog; - #endif +#endif -} // nowide +} // namespace nowide } // namespace boost #ifdef BOOST_MSVC -# pragma warning(pop) +#pragma warning(pop) #endif +#ifdef BOOST_WINDOWS +#include // pops abi_prefix.hpp pragmas +#endif #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/replacement.hpp b/xs/src/boost/nowide/replacement.hpp new file mode 100644 index 0000000000..4f1162777c --- /dev/null +++ b/xs/src/boost/nowide/replacement.hpp @@ -0,0 +1,19 @@ +// +// Copyright (c) 2018 Artyom Beilis (Tonkikh) +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_REPLACEMENT_HPP_INCLUDED +#define BOOST_NOWIDE_REPLACEMENT_HPP_INCLUDED + +/// @file + +/// \def BOOST_NOWIDE_REPLACEMENT_CHARACTER +/// Unicode character to be used to replace invalid UTF-8 sequences +#ifndef BOOST_NOWIDE_REPLACEMENT_CHARACTER +#define BOOST_NOWIDE_REPLACEMENT_CHARACTER 0xFFFD +#endif + +#endif // boost/nowide/replacement.hpp diff --git a/xs/src/boost/nowide/stackstring.hpp b/xs/src/boost/nowide/stackstring.hpp old mode 100755 new mode 100644 index f1445eac3b..129de78db3 --- a/xs/src/boost/nowide/stackstring.hpp +++ b/xs/src/boost/nowide/stackstring.hpp @@ -2,153 +2,211 @@ // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BOOST_NOWIDE_DETAILS_WIDESTR_H_INCLUDED -#define BOOST_NOWIDE_DETAILS_WIDESTR_H_INCLUDED +#ifndef BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED +#define BOOST_NOWIDE_STACKSTRING_HPP_INCLUDED + #include -#include -#include +#include +#include +#include namespace boost { namespace nowide { -/// -/// \brief A class that allows to create a temporary wide or narrow UTF strings from -/// wide or narrow UTF source. -/// -/// It uses on stack buffer of the string is short enough -/// and allocated a buffer on the heap if the size of the buffer is too small -/// -template -class basic_stackstring { -public: - - static const size_t buffer_size = BufferSize; - typedef CharOut output_char; - typedef CharIn input_char; - - basic_stackstring(basic_stackstring const &other) : - mem_buffer_(0) + /// + /// \brief A class that allows to create a temporary wide or narrow UTF strings from + /// wide or narrow UTF source. + /// + /// It uses a stack buffer if the string is short enough + /// otherwise allocates a buffer on the heap. + /// + /// Invalid UTF characters are replaced by the substitution character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// + /// If a NULL pointer is passed to the constructor or convert method, NULL will be returned by c_str. + /// Similarily a default constructed stackstring will return NULL on calling c_str. + /// + template + class basic_stackstring { - clear(); - if(other.mem_buffer_) { - size_t len = 0; - while(other.mem_buffer_[len]) - len ++; - mem_buffer_ = new output_char[len + 1]; - memcpy(mem_buffer_,other.mem_buffer_,sizeof(output_char) * (len+1)); + public: + /// Size of the stack buffer + static const size_t buffer_size = BufferSize; + /// Type of the output character (converted to) + using output_char = CharOut; + /// Type of the input character (converted from) + using input_char = CharIn; + + /// Creates a NULL stackstring + basic_stackstring() : data_(NULL) + { + buffer_[0] = 0; } - else { - memcpy(buffer_,other.buffer_,buffer_size * sizeof(output_char)); + /// Convert the NULL terminated string input and store in internal buffer + /// If input is NULL, nothing will be stored + explicit basic_stackstring(const input_char* input) : data_(NULL) + { + convert(input); } - } - - void swap(basic_stackstring &other) - { - std::swap(mem_buffer_,other.mem_buffer_); - for(size_t i=0;i::max_width + 1; + data_ = new output_char[max_output_size]; + const bool success = utf::convert_buffer(data_, max_output_size, begin, end) == data_; + assert(success); + (void)success; + } } - return true; + return get(); + } + /// Return the converted, NULL-terminated string or NULL if no string was converted + output_char* get() + { + return data_; + } + /// Return the converted, NULL-terminated string or NULL if no string was converted + const output_char* get() const + { + return data_; + } + /// Reset the internal buffer to NULL + void clear() + { + if(!uses_stack_memory()) + delete[] data_; + data_ = NULL; + } + /// Swap lhs with rhs + friend void swap(basic_stackstring& lhs, basic_stackstring& rhs) + { + if(lhs.uses_stack_memory()) + { + if(rhs.uses_stack_memory()) + { + for(size_t i = 0; i < buffer_size; i++) + std::swap(lhs.buffer_[i], rhs.buffer_[i]); + } else + { + lhs.data_ = rhs.data_; + rhs.data_ = rhs.buffer_; + for(size_t i = 0; i < buffer_size; i++) + rhs.buffer_[i] = lhs.buffer_[i]; + } + } else if(rhs.uses_stack_memory()) + { + rhs.data_ = lhs.data_; + lhs.data_ = lhs.buffer_; + for(size_t i = 0; i < buffer_size; i++) + lhs.buffer_[i] = rhs.buffer_[i]; + } else + std::swap(lhs.data_, rhs.data_); } - } - output_char *c_str() - { - if(mem_buffer_) - return mem_buffer_; - return buffer_; - } - output_char const *c_str() const - { - if(mem_buffer_) - return mem_buffer_; - return buffer_; - } - void clear() - { - if(mem_buffer_) { - delete [] mem_buffer_; - mem_buffer_=0; + protected: + /// True if the stack memory is used + bool uses_stack_memory() const + { + return data_ == buffer_; + } + /// Return the current length of the string excluding the NULL terminator + /// If NULL is stored returns NULL + size_t length() const + { + if(!data_) + return 0; + size_t len = 0; + while(data_[len]) + len++; + return len; } - buffer_[0] = 0; - } - ~basic_stackstring() - { - clear(); - } -private: - static size_t get_space(size_t insize,size_t outsize,size_t in) - { - if(insize <= outsize) - return in; - else if(insize == 2 && outsize == 1) - return 3 * in; - else if(insize == 4 && outsize == 1) - return 4 * in; - else // if(insize == 4 && outsize == 2) - return 2 * in; - } - output_char buffer_[buffer_size]; - output_char *mem_buffer_; -}; //basic_stackstring -/// -/// Convenience typedef -/// -typedef basic_stackstring wstackstring; -/// -/// Convenience typedef -/// -typedef basic_stackstring stackstring; -/// -/// Convenience typedef -/// -typedef basic_stackstring wshort_stackstring; -/// -/// Convenience typedef -/// -typedef basic_stackstring short_stackstring; + private: + output_char buffer_[buffer_size]; + output_char* data_; + }; // basic_stackstring + /// + /// Convenience typedef + /// + using wstackstring = basic_stackstring; + /// + /// Convenience typedef + /// + using stackstring = basic_stackstring; + /// + /// Convenience typedef + /// + using wshort_stackstring = basic_stackstring; + /// + /// Convenience typedef + /// + using short_stackstring = basic_stackstring; -} // nowide +} // namespace nowide } // namespace boost #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/stat.hpp b/xs/src/boost/nowide/stat.hpp new file mode 100644 index 0000000000..4a55401d86 --- /dev/null +++ b/xs/src/boost/nowide/stat.hpp @@ -0,0 +1,65 @@ +// +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_STAT_HPP_INCLUDED +#define BOOST_NOWIDE_STAT_HPP_INCLUDED + +#include +#include +// Include after sys/types.h +#include + +#if defined(__MINGW32__) && defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x0601 +/// Forward declaration in case MinGW32 is used and __MSVCRT_VERSION__ is defined lower than 6.1 +struct __stat64; +#endif + +namespace boost { +namespace nowide { +#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) + // Note: `using x = struct ::stat` causes a bogus warning in GCC < 11 + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66159 + + typedef struct ::stat stat_t; + typedef struct ::stat posix_stat_t; + + using ::stat; +#else + /// \brief Typedef for the file info structure. + /// Able to hold 64 bit filesize and timestamps on Windows and usually also on other 64 Bit systems + /// This allows to write portable code with option LFS support + typedef struct ::__stat64 stat_t; + /// \brief Typedef for the file info structure used in the POSIX stat call + /// Resolves to `struct _stat` on Windows and `struct stat` otherwise + /// This allows to write portable code using the default stat function + typedef struct ::_stat posix_stat_t; + + /// \cond INTERNAL + namespace detail { + BOOST_NOWIDE_DECL int stat(const char* path, posix_stat_t* buffer, size_t buffer_size); + } + /// \endcond + + /// + /// \brief UTF-8 aware stat function, returns 0 on success + /// + /// Return information about a file from an UTF-8 encoded path + /// + BOOST_NOWIDE_DECL int stat(const char* path, stat_t* buffer); + /// + /// \brief UTF-8 aware stat function, returns 0 on success + /// + /// Return information about a file from an UTF-8 encoded path + /// + inline int stat(const char* path, posix_stat_t* buffer) + { + return detail::stat(path, buffer, sizeof(*buffer)); + } +#endif +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/system.hpp b/xs/src/boost/nowide/system.hpp deleted file mode 100755 index a1fc975059..0000000000 --- a/xs/src/boost/nowide/system.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) 2012 Artyom Beilis (Tonkikh) -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef BOOST_NOWIDE_CSTDLIB_HPP -#define BOOST_NOWIDE_CSTDLIB_HPP - -#include -#include -#include -namespace boost { -namespace nowide { - -#if !defined(BOOST_WINDOWS) && !defined(BOOST_NOWIDE_DOXYGEN) - -using ::system; - -#else // Windows - -/// -/// Same as std::system but cmd is UTF-8. -/// -/// If the input is not valid UTF-8, -1 returned and errno set to EINVAL -/// -inline int system(char const *cmd) -{ - if(!cmd) - return _wsystem(0); - wstackstring wcmd; - if(!wcmd.convert(cmd)) { - errno = EINVAL; - return -1; - } - return _wsystem(wcmd.c_str()); -} - -#endif -} // nowide -} // namespace boost - -#endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/boost/nowide/utf/convert.hpp b/xs/src/boost/nowide/utf/convert.hpp new file mode 100644 index 0000000000..242b7d1277 --- /dev/null +++ b/xs/src/boost/nowide/utf/convert.hpp @@ -0,0 +1,98 @@ +// +// Copyright (c) 2012 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_UTF_CONVERT_HPP_INCLUDED +#define BOOST_NOWIDE_UTF_CONVERT_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace boost { +namespace nowide { + namespace utf { + + /// Return the length of the given string in code units. + /// That is the number of elements of type Char until the first NULL character. + /// Equivalent to `std::strlen(s)` but can handle wide-strings + template + size_t strlen(const Char* s) + { + const Char* end = s; + while(*end) + end++; + return end - s; + } + + /// Convert a buffer of UTF sequences in the range [source_begin, source_end) + /// from \a CharIn to \a CharOut to the output \a buffer of size \a buffer_size. + /// + /// \return original buffer containing the NULL terminated string or NULL + /// + /// If there is not enough room in the buffer NULL is returned, and the content of the buffer is undefined. + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + template + CharOut* + convert_buffer(CharOut* buffer, size_t buffer_size, const CharIn* source_begin, const CharIn* source_end) + { + CharOut* rv = buffer; + if(buffer_size == 0) + return nullptr; + buffer_size--; + while(source_begin != source_end) + { + code_point c = utf_traits::decode(source_begin, source_end); + if(c == illegal || c == incomplete) + { + c = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + size_t width = utf_traits::width(c); + if(buffer_size < width) + { + rv = NULL; + break; + } + buffer = utf_traits::encode(c, buffer); + buffer_size -= width; + } + *buffer++ = 0; + return rv; + } + + /// Convert the UTF sequences in range [begin, end) from \a CharIn to \a CharOut + /// and return it as a string + /// + /// Any illegal sequences are replaced with the replacement character, see #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// \tparam CharOut Output character type + template + std::basic_string convert_string(const CharIn* begin, const CharIn* end) + { + std::basic_string result; + result.reserve(end - begin); + using inserter_type = std::back_insert_iterator>; + inserter_type inserter(result); + code_point c; + while(begin != end) + { + c = utf_traits::decode(begin, end); + if(c == illegal || c == incomplete) + { + c = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + utf_traits::encode(c, inserter); + } + return result; + } + + } // namespace utf +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/utf/utf.hpp b/xs/src/boost/nowide/utf/utf.hpp new file mode 100644 index 0000000000..acdc57a562 --- /dev/null +++ b/xs/src/boost/nowide/utf/utf.hpp @@ -0,0 +1,455 @@ +// +// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2020 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_NOWIDE_UTF_HPP_INCLUDED +#define BOOST_NOWIDE_UTF_HPP_INCLUDED + +#include +#include + +namespace boost { +namespace nowide { + /// + /// \brief Namespace that holds basic operations on UTF encoded sequences + /// + /// All functions defined in this namespace do not require linking with Boost.Nowide library. + /// Extracted from Boost.Locale + /// + namespace utf { + + /// + /// \brief The integral type that can hold a Unicode code point + /// + using code_point = uint32_t; + + /// + /// \brief Special constant that defines illegal code point + /// + static const code_point illegal = 0xFFFFFFFFu; + + /// + /// \brief Special constant that defines incomplete code point + /// + static const code_point incomplete = 0xFFFFFFFEu; + + /// + /// \brief the function checks if \a v is a valid code point + /// + inline bool is_valid_codepoint(code_point v) + { + if(v > 0x10FFFF) + return false; + if(0xD800 <= v && v <= 0xDFFF) // surrogates + return false; + return true; + } + +#ifdef BOOST_NOWIDE_DOXYGEN + /// + /// \brief UTF Traits class - functions to convert UTF sequences to and from Unicode code points + /// + template + struct utf_traits + { + /// + /// The type of the character + /// + using char_type = CharType; + /// + /// Read one code point from the range [p,e) and return it. + /// + /// - If the sequence that was read is incomplete sequence returns \ref incomplete, + /// - If illegal sequence detected returns \ref illegal + /// + /// Requirements + /// + /// - Iterator is valid input iterator + /// + /// Postconditions + /// + /// - p points to the last consumed character + /// + template + static code_point decode(Iterator& p, Iterator e); + + /// + /// Maximal width of valid sequence in the code units: + /// + /// - UTF-8 - 4 + /// - UTF-16 - 2 + /// - UTF-32 - 1 + /// + static const int max_width; + /// + /// The width of specific code point in the code units. + /// + /// Requirement: value is a valid Unicode code point + /// Returns value in range [1..max_width] + /// + static int width(code_point value); + + /// + /// Get the size of the trail part of variable length encoded sequence. + /// + /// Returns -1 if C is not valid lead character + /// + static int trail_length(char_type c); + /// + /// Returns true if c is trail code unit, always false for UTF-32 + /// + static bool is_trail(char_type c); + /// + /// Returns true if c is lead code unit, always true of UTF-32 + /// + static bool is_lead(char_type c); + + /// + /// Convert valid Unicode code point \a value to the UTF sequence. + /// + /// Requirements: + /// + /// - \a value is valid code point + /// - \a out is an output iterator should be able to accept at least width(value) units + /// + /// Returns the iterator past the last written code unit. + /// + template + static Iterator encode(code_point value, Iterator out); + /// + /// Decodes valid UTF sequence that is pointed by p into code point. + /// + /// If the sequence is invalid or points to end the behavior is undefined + /// + template + static code_point decode_valid(Iterator& p); + }; + +#else + + template + struct utf_traits; + + template + struct utf_traits + { + using char_type = CharType; + + static int trail_length(char_type ci) + { + unsigned char c = ci; + if(c < 128) + return 0; + if(BOOST_UNLIKELY(c < 194)) + return -1; + if(c < 224) + return 1; + if(c < 240) + return 2; + if(BOOST_LIKELY(c <= 244)) + return 3; + return -1; + } + + static const int max_width = 4; + + static int width(code_point value) + { + if(value <= 0x7F) + { + return 1; + } else if(value <= 0x7FF) + { + return 2; + } else if(BOOST_LIKELY(value <= 0xFFFF)) + { + return 3; + } else + { + return 4; + } + } + + static bool is_trail(char_type ci) + { + unsigned char c = ci; + return (c & 0xC0) == 0x80; + } + + static bool is_lead(char_type ci) + { + return !is_trail(ci); + } + + template + static code_point decode(Iterator& p, Iterator e) + { + if(BOOST_UNLIKELY(p == e)) + return incomplete; + + unsigned char lead = *p++; + + // First byte is fully validated here + int trail_size = trail_length(lead); + + if(BOOST_UNLIKELY(trail_size < 0)) + return illegal; + + // OK as only ASCII may be of size = 0 + // also optimize for ASCII text + if(trail_size == 0) + return lead; + + code_point c = lead & ((1 << (6 - trail_size)) - 1); + + // Read the rest + unsigned char tmp; + switch(trail_size) + { + case 3: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + BOOST_NOWIDE_FALLTHROUGH; + case 2: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + BOOST_NOWIDE_FALLTHROUGH; + case 1: + if(BOOST_UNLIKELY(p == e)) + return incomplete; + tmp = *p++; + if(!is_trail(tmp)) + return illegal; + c = (c << 6) | (tmp & 0x3F); + } + + // Check code point validity: + // - no surrogates and valid range + // - most compact representation + if(BOOST_UNLIKELY(!is_valid_codepoint(c)) || BOOST_UNLIKELY(width(c) != trail_size + 1)) + { + p -= trail_size; + return illegal; + } + + return c; + } + + template + static code_point decode_valid(Iterator& p) + { + unsigned char lead = *p++; + if(lead < 192) + return lead; + + int trail_size; + + if(lead < 224) + trail_size = 1; + else if(BOOST_LIKELY(lead < 240)) // non-BMP rare + trail_size = 2; + else + trail_size = 3; + + code_point c = lead & ((1 << (6 - trail_size)) - 1); + + switch(trail_size) + { + case 3: c = (c << 6) | (static_cast(*p++) & 0x3F); BOOST_NOWIDE_FALLTHROUGH; + case 2: c = (c << 6) | (static_cast(*p++) & 0x3F); BOOST_NOWIDE_FALLTHROUGH; + case 1: c = (c << 6) | (static_cast(*p++) & 0x3F); + } + + return c; + } + + template + static Iterator encode(code_point value, Iterator out) + { + if(value <= 0x7F) + { + *out++ = static_cast(value); + } else if(value <= 0x7FF) + { + *out++ = static_cast((value >> 6) | 0xC0); + *out++ = static_cast((value & 0x3F) | 0x80); + } else if(BOOST_LIKELY(value <= 0xFFFF)) + { + *out++ = static_cast((value >> 12) | 0xE0); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } else + { + *out++ = static_cast((value >> 18) | 0xF0); + *out++ = static_cast(((value >> 12) & 0x3F) | 0x80); + *out++ = static_cast(((value >> 6) & 0x3F) | 0x80); + *out++ = static_cast((value & 0x3F) | 0x80); + } + return out; + } + }; // utf8 + + template + struct utf_traits + { + using char_type = CharType; + + // See RFC 2781 + static bool is_single_codepoint(uint16_t x) + { + // Ranges [U+0000, 0+D7FF], [U+E000, U+FFFF] are numerically equal in UTF-16 + return x <= 0xD7FF || x >= 0xE000; + } + static bool is_first_surrogate(uint16_t x) + { + // Range [U+D800, 0+DBFF]: High surrogate + return 0xD800 <= x && x <= 0xDBFF; + } + static bool is_second_surrogate(uint16_t x) + { + // Range [U+DC00, 0+DFFF]: Low surrogate + return 0xDC00 <= x && x <= 0xDFFF; + } + static code_point combine_surrogate(uint16_t w1, uint16_t w2) + { + return ((code_point(w1 & 0x3FF) << 10) | (w2 & 0x3FF)) + 0x10000; + } + static int trail_length(char_type c) + { + if(is_first_surrogate(c)) + return 1; + if(is_second_surrogate(c)) + return -1; + return 0; + } + /// Return true if c is trail code unit, always false for UTF-32 + static bool is_trail(char_type c) + { + return is_second_surrogate(c); + } + /// Return true if c is lead code unit, always true of UTF-32 + static bool is_lead(char_type c) + { + return !is_second_surrogate(c); + } + + template + static code_point decode(It& current, It last) + { + if(BOOST_UNLIKELY(current == last)) + return incomplete; + uint16_t w1 = *current++; + if(BOOST_LIKELY(is_single_codepoint(w1))) + { + return w1; + } + // Now it's either a high or a low surrogate, the latter is invalid + if(w1 >= 0xDC00) + return illegal; + if(current == last) + return incomplete; + uint16_t w2 = *current++; + if(!is_second_surrogate(w2)) + return illegal; + return combine_surrogate(w1, w2); + } + template + static code_point decode_valid(It& current) + { + uint16_t w1 = *current++; + if(BOOST_LIKELY(is_single_codepoint(w1))) + { + return w1; + } + uint16_t w2 = *current++; + return combine_surrogate(w1, w2); + } + + static const int max_width = 2; + static int width(code_point u) // LCOV_EXCL_LINE + { + return u >= 0x10000 ? 2 : 1; + } + template + static It encode(code_point u, It out) + { + if(BOOST_LIKELY(u <= 0xFFFF)) + { + *out++ = static_cast(u); + } else + { + u -= 0x10000; + *out++ = static_cast(0xD800 | (u >> 10)); + *out++ = static_cast(0xDC00 | (u & 0x3FF)); + } + return out; + } + }; // utf16; + + template + struct utf_traits + { + using char_type = CharType; + static int trail_length(char_type c) + { + if(is_valid_codepoint(c)) + return 0; + return -1; + } + static bool is_trail(char_type /*c*/) + { + return false; + } + static bool is_lead(char_type /*c*/) + { + return true; + } + + template + static code_point decode_valid(It& current) + { + return *current++; + } + + template + static code_point decode(It& current, It last) + { + if(BOOST_UNLIKELY(current == last)) + return incomplete; + code_point c = *current++; + if(BOOST_UNLIKELY(!is_valid_codepoint(c))) + return illegal; + return c; + } + static const int max_width = 1; + static int width(code_point /*u*/) + { + return 1; + } + template + static It encode(code_point u, It out) + { + *out++ = static_cast(u); + return out; + } + }; // utf32 + +#endif + + } // namespace utf +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/utf8_codecvt.hpp b/xs/src/boost/nowide/utf8_codecvt.hpp old mode 100755 new mode 100644 index 15ec0be8fe..ebe0bccc14 --- a/xs/src/boost/nowide/utf8_codecvt.hpp +++ b/xs/src/boost/nowide/utf8_codecvt.hpp @@ -1,499 +1,376 @@ -// -// Copyright (c) 2015 Artyom Beilis (Tonkikh) -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef BOOST_NOWIDE_UTF8_CODECVT_HPP -#define BOOST_NOWIDE_UTF8_CODECVT_HPP - -#include -#include -#include -#include - -namespace boost { -namespace nowide { - -// -// Make sure that mbstate can keep 16 bit of UTF-16 sequence -// -BOOST_STATIC_ASSERT(sizeof(std::mbstate_t)>=2); - -#ifdef _MSC_VER -// MSVC do_length is non-standard it counts wide characters instead of narrow and does not change mbstate -#define BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST -#endif - -template -class utf8_codecvt; - -template -class utf8_codecvt : public std::codecvt -{ -public: - utf8_codecvt(size_t refs = 0) : std::codecvt(refs) - { - } -protected: - - typedef CharType uchar; - - virtual std::codecvt_base::result do_unshift(std::mbstate_t &s,char *from,char * /*to*/,char *&next) const - { - boost::uint16_t &state = *reinterpret_cast(&s); -#ifdef DEBUG_CODECVT - std::cout << "Entering unshift " << std::hex << state << std::dec << std::endl; -#endif - if(state != 0) - return std::codecvt_base::error; - next=from; - return std::codecvt_base::ok; - } - virtual int do_encoding() const throw() - { - return 0; - } - virtual int do_max_length() const throw() - { - return 4; - } - virtual bool do_always_noconv() const throw() - { - return false; - } - - virtual int - do_length( std::mbstate_t - #ifdef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - const - #endif - &std_state, - char const *from, - char const *from_end, - size_t max) const - { - #ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - char const *save_from = from; - boost::uint16_t &state = *reinterpret_cast(&std_state); - #else - size_t save_max = max; - boost::uint16_t state = *reinterpret_cast(&std_state); - #endif - while(max > 0 && from < from_end){ - char const *prev_from = from; - boost::uint32_t ch=boost::locale::utf::utf_traits::decode(from,from_end); - if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) { - from = prev_from; - break; - } - max --; - if(ch > 0xFFFF) { - if(state == 0) { - from = prev_from; - state = 1; - } - else { - state = 0; - } - } - } - #ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - return from - save_from; - #else - return save_max - max; - #endif - } - - - virtual std::codecvt_base::result - do_in( std::mbstate_t &std_state, - char const *from, - char const *from_end, - char const *&from_next, - uchar *to, - uchar *to_end, - uchar *&to_next) const - { - std::codecvt_base::result r=std::codecvt_base::ok; - - // mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT()) - // according to standard. We use it to keep a flag 0/1 for surrogate pair writing - // - // if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd - // and first pair is written, but no input consumed - boost::uint16_t &state = *reinterpret_cast(&std_state); - while(to < to_end && from < from_end) - { -#ifdef DEBUG_CODECVT - std::cout << "Entering IN--------------" << std::endl; - std::cout << "State " << std::hex << state <::decode(from,from_end); - - if(ch==boost::locale::utf::illegal) { - from = from_saved; - r=std::codecvt_base::error; - break; - } - if(ch==boost::locale::utf::incomplete) { - from = from_saved; - r=std::codecvt_base::partial; - break; - } - // Normal codepoints go directly to stream - if(ch <= 0xFFFF) { - *to++=ch; - } - else { - // for other codepoints we do following - // - // 1. We can't consume our input as we may find ourselves - // in state where all input consumed but not all output written,i.e. only - // 1st pair is written - // 2. We only write first pair and mark this in the state, we also revert back - // the from pointer in order to make sure this codepoint would be read - // once again and then we would consume our input together with writing - // second surrogate pair - ch-=0x10000; - boost::uint16_t vh = ch >> 10; - boost::uint16_t vl = ch & 0x3FF; - boost::uint16_t w1 = vh + 0xD800; - boost::uint16_t w2 = vl + 0xDC00; - if(state == 0) { - from = from_saved; - *to++ = w1; - state = 1; - } - else { - *to++ = w2; - state = 0; - } - } - } - from_next=from; - to_next=to; - if(r == std::codecvt_base::ok && (from!=from_end || state!=0)) - r = std::codecvt_base::partial; -#ifdef DEBUG_CODECVT - std::cout << "Returning "; - switch(r) { - case std::codecvt_base::ok: - std::cout << "ok" << std::endl; - break; - case std::codecvt_base::partial: - std::cout << "partial" << std::endl; - break; - case std::codecvt_base::error: - std::cout << "error" << std::endl; - break; - default: - std::cout << "other" << std::endl; - break; - } - std::cout << "State " << std::hex << state <=2 in order - // to be able to store first observerd surrogate pair - // - // State: state!=0 - a first surrogate pair was observerd (state = first pair), - // we expect the second one to come and then zero the state - /// - boost::uint16_t &state = *reinterpret_cast(&std_state); - while(to < to_end && from < from_end) - { -#ifdef DEBUG_CODECVT - std::cout << "Entering OUT --------------" << std::endl; - std::cout << "State " << std::hex << state <::width(ch); - if(to_end - to < len) { - r=std::codecvt_base::partial; - break; - } - to = boost::locale::utf::utf_traits::encode(ch,to); - state = 0; - from++; - } - from_next=from; - to_next=to; - if(r==std::codecvt_base::ok && from!=from_end) - r = std::codecvt_base::partial; -#ifdef DEBUG_CODECVT - std::cout << "Returning "; - switch(r) { - case std::codecvt_base::ok: - std::cout << "ok" << std::endl; - break; - case std::codecvt_base::partial: - std::cout << "partial" << std::endl; - break; - case std::codecvt_base::error: - std::cout << "error" << std::endl; - break; - default: - std::cout << "other" << std::endl; - break; - } - std::cout << "State " << std::hex << state < -class utf8_codecvt : public std::codecvt -{ -public: - utf8_codecvt(size_t refs = 0) : std::codecvt(refs) - { - } -protected: - - typedef CharType uchar; - - virtual std::codecvt_base::result do_unshift(std::mbstate_t &/*s*/,char *from,char * /*to*/,char *&next) const - { - next=from; - return std::codecvt_base::ok; - } - virtual int do_encoding() const throw() - { - return 0; - } - virtual int do_max_length() const throw() - { - return 4; - } - virtual bool do_always_noconv() const throw() - { - return false; - } - - virtual int - do_length( std::mbstate_t - #ifdef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - const - #endif - &/*state*/, - char const *from, - char const *from_end, - size_t max) const - { - #ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - char const *start_from = from; - #else - size_t save_max = max; - #endif - - while(max > 0 && from < from_end){ - char const *save_from = from; - boost::uint32_t ch=boost::locale::utf::utf_traits::decode(from,from_end); - if(ch==boost::locale::utf::incomplete || ch==boost::locale::utf::illegal) { - from = save_from; - break; - } - max--; - } - #ifndef BOOST_NOWIDE_DO_LENGTH_MBSTATE_CONST - return from - start_from; - #else - return save_max - max; - #endif - } - - - virtual std::codecvt_base::result - do_in( std::mbstate_t &/*state*/, - char const *from, - char const *from_end, - char const *&from_next, - uchar *to, - uchar *to_end, - uchar *&to_next) const - { - std::codecvt_base::result r=std::codecvt_base::ok; - - // mbstate_t is POD type and should be initialized to 0 (i.a. state = stateT()) - // according to standard. We use it to keep a flag 0/1 for surrogate pair writing - // - // if 0 no code above >0xFFFF observed, of 1 a code above 0xFFFF observerd - // and first pair is written, but no input consumed - while(to < to_end && from < from_end) - { -#ifdef DEBUG_CODECVT - std::cout << "Entering IN--------------" << std::endl; - std::cout << "State " << std::hex << state <::decode(from,from_end); - - if(ch==boost::locale::utf::illegal) { - r=std::codecvt_base::error; - from = from_saved; - break; - } - if(ch==boost::locale::utf::incomplete) { - r=std::codecvt_base::partial; - from=from_saved; - break; - } - *to++=ch; - } - from_next=from; - to_next=to; - if(r == std::codecvt_base::ok && from!=from_end) - r = std::codecvt_base::partial; -#ifdef DEBUG_CODECVT - std::cout << "Returning "; - switch(r) { - case std::codecvt_base::ok: - std::cout << "ok" << std::endl; - break; - case std::codecvt_base::partial: - std::cout << "partial" << std::endl; - break; - case std::codecvt_base::error: - std::cout << "error" << std::endl; - break; - default: - std::cout << "other" << std::endl; - break; - } - std::cout << "State " << std::hex << state <::width(ch); - if(to_end - to < len) { - r=std::codecvt_base::partial; - break; - } - to = boost::locale::utf::utf_traits::encode(ch,to); - from++; - } - from_next=from; - to_next=to; - if(r==std::codecvt_base::ok && from!=from_end) - r = std::codecvt_base::partial; -#ifdef DEBUG_CODECVT - std::cout << "Returning "; - switch(r) { - case std::codecvt_base::ok: - std::cout << "ok" << std::endl; - break; - case std::codecvt_base::partial: - std::cout << "partial" << std::endl; - break; - case std::codecvt_base::error: - std::cout << "error" << std::endl; - break; - default: - std::cout << "other" << std::endl; - break; - } - std::cout << "State " << std::hex << state < +#include +#include +#include +#include + +namespace boost { +namespace nowide { + + static_assert(sizeof(std::mbstate_t) >= 2, "mbstate_t is to small to store an UTF-16 codepoint"); + namespace detail { + // Avoid including cstring for std::memcpy + inline void copy_uint16_t(void* dst, const void* src) + { + unsigned char* cdst = static_cast(dst); + const unsigned char* csrc = static_cast(src); + cdst[0] = csrc[0]; + cdst[1] = csrc[1]; + } + inline std::uint16_t read_state(const std::mbstate_t& src) + { + std::uint16_t dst; + copy_uint16_t(&dst, &src); + return dst; + } + inline void write_state(std::mbstate_t& dst, const std::uint16_t src) + { + copy_uint16_t(&dst, &src); + } + } // namespace detail + + /// std::codecvt implementation that converts between UTF-8 and UTF-16 or UTF-32 + /// + /// @tparam CharSize Determines the encoding: 2 for UTF-16, 4 for UTF-32 + /// + /// Invalid sequences are replaced by #BOOST_NOWIDE_REPLACEMENT_CHARACTER + /// A trailing incomplete sequence will result in a return value of std::codecvt::partial + template + class utf8_codecvt; + + BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN + /// Specialization for the UTF-8 <-> UTF-16 variant of the std::codecvt implementation + template + class BOOST_SYMBOL_VISIBLE utf8_codecvt : public std::codecvt + { + public: + static_assert(sizeof(CharType) >= 2, "CharType must be able to store UTF16 code point"); + + utf8_codecvt(size_t refs = 0) : std::codecvt(refs) + {} + BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END + + protected: + using uchar = CharType; + + std::codecvt_base::result do_unshift(std::mbstate_t& s, char* from, char* /*to*/, char*& next) const override + { + if(detail::read_state(s) != 0) + return std::codecvt_base::error; + next = from; + return std::codecvt_base::ok; + } + int do_encoding() const noexcept override + { + return 0; + } + int do_max_length() const noexcept override + { + return 4; + } + bool do_always_noconv() const noexcept override + { + return false; + } + + // LCOV_EXCL_START + int do_length(std::mbstate_t& std_state, const char* from, const char* from_end, size_t max) const override + { + // LCOV_EXCL_STOP + using utf16_traits = utf::utf_traits; + std::uint16_t state = detail::read_state(std_state); + const char* save_from = from; + if(state && max > 0) + { + max--; + state = 0; + } + while(max > 0 && from < from_end) + { + const char* prev_from = from; + std::uint32_t ch = utf::utf_traits::decode(from, from_end); + if(ch == utf::illegal) + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } else if(ch == utf::incomplete) + { + from = prev_from; + break; + } + // If we can't write the char, we have to save the low surrogate in state + if(BOOST_LIKELY(static_cast(utf16_traits::width(ch)) <= max)) + { + max -= utf16_traits::width(ch); + } else + { + static_assert(utf16_traits::max_width == 2, "Required for below"); + std::uint16_t tmpOut[2]{}; + utf16_traits::encode(ch, tmpOut); + state = tmpOut[1]; + break; + } + } + detail::write_state(std_state, state); + return static_cast(from - save_from); + } + + std::codecvt_base::result do_in(std::mbstate_t& std_state, // LCOV_EXCL_LINE + const char* from, + const char* from_end, + const char*& from_next, + uchar* to, + uchar* to_end, + uchar*& to_next) const override + { + std::codecvt_base::result r = std::codecvt_base::ok; + using utf16_traits = utf::utf_traits; + + // mbstate_t is POD type and should be initialized to 0 (i.e. state = stateT()) + // according to standard. + // We use it to store a low surrogate if it was not yet written, else state is 0 + std::uint16_t state = detail::read_state(std_state); + // Write low surrogate if present + if(state && to < to_end) + { + *to++ = static_cast(state); + state = 0; + } + while(to < to_end && from < from_end) + { + const char* from_saved = from; + + uint32_t ch = utf::utf_traits::decode(from, from_end); + + if(ch == utf::illegal) + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } else if(ch == utf::incomplete) + { + from = from_saved; + r = std::codecvt_base::partial; + break; + } + // If the encoded char fits, write directly, else safe the low surrogate in state + if(BOOST_LIKELY(utf16_traits::width(ch) <= to_end - to)) + { + to = utf16_traits::encode(ch, to); + } else + { + static_assert(utf16_traits::max_width == 2, "Required for below"); + std::uint16_t tmpOut[2]{}; + utf16_traits::encode(ch, tmpOut); + *to++ = static_cast(tmpOut[0]); + state = tmpOut[1]; + break; + } + } + from_next = from; + to_next = to; + if(r == std::codecvt_base::ok && (from != from_end || state != 0)) + r = std::codecvt_base::partial; + detail::write_state(std_state, state); + return r; + } + + std::codecvt_base::result do_out(std::mbstate_t& std_state, + const uchar* from, + const uchar* from_end, + const uchar*& from_next, + char* to, + char* to_end, + char*& to_next) const override + { + std::codecvt_base::result r = std::codecvt_base::ok; + using utf16_traits = utf::utf_traits; + // mbstate_t is POD type and should be initialized to 0 + // (i.e. state = stateT()) according to standard. + // We use it to store the first observed surrogate pair, or 0 if there is none yet + std::uint16_t state = detail::read_state(std_state); + for(; to < to_end && from < from_end; ++from) + { + std::uint32_t ch = 0; + if(state != 0) + { + // We have a high surrogate, so now there should be a low surrogate + std::uint16_t w1 = state; + std::uint16_t w2 = *from; + if(BOOST_LIKELY(utf16_traits::is_trail(w2))) + { + ch = utf16_traits::combine_surrogate(w1, w2); + } else + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + } else + { + std::uint16_t w1 = *from; + if(BOOST_LIKELY(utf16_traits::is_single_codepoint(w1))) + { + ch = w1; + } else if(BOOST_LIKELY(utf16_traits::is_first_surrogate(w1))) + { + // Store into state and continue at next character + state = w1; + continue; + } else + { + // Neither a single codepoint nor a high surrogate so must be low surrogate. + // This is an error -> Replace character + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + } + assert(utf::is_valid_codepoint(ch)); // Any valid UTF16 sequence is a valid codepoint + int len = utf::utf_traits::width(ch); + if(to_end - to < len) + { + r = std::codecvt_base::partial; + break; + } + to = utf::utf_traits::encode(ch, to); + state = 0; + } + from_next = from; + to_next = to; + if(r == std::codecvt_base::ok && (from != from_end || state != 0)) + r = std::codecvt_base::partial; + detail::write_state(std_state, state); + return r; + } + }; + + BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_BEGIN + /// Specialization for the UTF-8 <-> UTF-32 variant of the std::codecvt implementation + template + class BOOST_SYMBOL_VISIBLE utf8_codecvt : public std::codecvt + { + public: + utf8_codecvt(size_t refs = 0) : std::codecvt(refs) + {} + BOOST_NOWIDE_SUPPRESS_UTF_CODECVT_DEPRECATION_END + + protected: + using uchar = CharType; + + std::codecvt_base::result + do_unshift(std::mbstate_t& /*s*/, char* from, char* /*to*/, char*& next) const override + { + next = from; + return std::codecvt_base::noconv; + } + int do_encoding() const noexcept override + { + return 0; + } + int do_max_length() const noexcept override + { + return 4; + } + bool do_always_noconv() const noexcept override + { + return false; + } + + int do_length(std::mbstate_t& /*state*/, const char* from, const char* from_end, size_t max) const override + { + const char* start_from = from; + + while(max > 0 && from < from_end) + { + const char* save_from = from; + std::uint32_t ch = utf::utf_traits::decode(from, from_end); + if(ch == utf::incomplete) + { + from = save_from; + break; + } else if(ch == utf::illegal) + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + max--; + } + return static_cast(from - start_from); + } + + std::codecvt_base::result do_in(std::mbstate_t& /*state*/, + const char* from, + const char* from_end, + const char*& from_next, + uchar* to, + uchar* to_end, + uchar*& to_next) const override + { + std::codecvt_base::result r = std::codecvt_base::ok; + + while(to < to_end && from < from_end) + { + const char* from_saved = from; + + uint32_t ch = utf::utf_traits::decode(from, from_end); + + if(ch == utf::illegal) + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } else if(ch == utf::incomplete) + { + r = std::codecvt_base::partial; + from = from_saved; + break; + } + *to++ = ch; + } + from_next = from; + to_next = to; + if(r == std::codecvt_base::ok && from != from_end) + r = std::codecvt_base::partial; + return r; + } + + std::codecvt_base::result do_out(std::mbstate_t& /*std_state*/, + const uchar* from, + const uchar* from_end, + const uchar*& from_next, + char* to, + char* to_end, + char*& to_next) const override + { + std::codecvt_base::result r = std::codecvt_base::ok; + while(to < to_end && from < from_end) + { + std::uint32_t ch = 0; + ch = *from; + if(!utf::is_valid_codepoint(ch)) + { + ch = BOOST_NOWIDE_REPLACEMENT_CHARACTER; + } + int len = utf::utf_traits::width(ch); + if(to_end - to < len) + { + r = std::codecvt_base::partial; + break; + } + to = utf::utf_traits::encode(ch, to); + from++; + } + from_next = from; + to_next = to; + if(r == std::codecvt_base::ok && from != from_end) + r = std::codecvt_base::partial; + return r; + } + }; + +} // namespace nowide +} // namespace boost + +#endif diff --git a/xs/src/boost/nowide/windows.hpp b/xs/src/boost/nowide/windows.hpp old mode 100755 new mode 100644 index 164ed55318..a5810be828 --- a/xs/src/boost/nowide/windows.hpp +++ b/xs/src/boost/nowide/windows.hpp @@ -2,38 +2,31 @@ // Copyright (c) 2012 Artyom Beilis (Tonkikh) // // Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at +// accompanying file LICENSE or copy at // http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_NOWIDE_WINDOWS_HPP_INCLUDED #define BOOST_NOWIDE_WINDOWS_HPP_INCLUDED -#include - -#ifdef BOOST_NOWIDE_USE_WINDOWS_H +#ifdef BOOST_USE_WINDOWS_H #include #else // -// These are function prototypes... Allow to to include windows.h +// These are function prototypes... Allow to avoid including windows.h // extern "C" { -__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void); -__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t *); -__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void); -__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(wchar_t const *,int *); -__declspec(dllimport) unsigned long __stdcall GetLastError(); -__declspec(dllimport) void* __stdcall LocalFree(void *); -__declspec(dllimport) int __stdcall SetEnvironmentVariableW(wchar_t const *,wchar_t const *); -__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(wchar_t const *,wchar_t *,unsigned long); - +__declspec(dllimport) wchar_t* __stdcall GetEnvironmentStringsW(void); +__declspec(dllimport) int __stdcall FreeEnvironmentStringsW(wchar_t*); +__declspec(dllimport) wchar_t* __stdcall GetCommandLineW(void); +__declspec(dllimport) wchar_t** __stdcall CommandLineToArgvW(const wchar_t*, int*); +__declspec(dllimport) unsigned long __stdcall GetLastError(); +__declspec(dllimport) void* __stdcall LocalFree(void*); +__declspec(dllimport) int __stdcall SetEnvironmentVariableW(const wchar_t*, const wchar_t*); +__declspec(dllimport) unsigned long __stdcall GetEnvironmentVariableW(const wchar_t*, wchar_t*, unsigned long); } -#endif - - +#endif #endif -/// -// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index b9f63ff875..178c7f6a3d 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -224,7 +224,8 @@ ExtrusionEntityCollection::items_count() const void ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval, bool preserve_ordering) const { - if (this->no_sort and preserve_ordering) { + if (this->no_sort && preserve_ordering) + { /// if we want to preserve ordering and we can't sort, break out the unsorted ones first. ExtrusionEntityCollection *unsortable = new ExtrusionEntityCollection(*this); retval->append(*unsortable); @@ -238,7 +239,9 @@ ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval, bool prese unsortable->append(**it); } } - } else { + } + else + { for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { if ((*it)->is_collection()) { ExtrusionEntityCollection* collection = dynamic_cast(*it); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 499a353fb9..ac49c4f4eb 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -197,7 +197,7 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) GCode::GCode() : placeholder_parser(NULL), enable_loop_clipping(true), enable_cooling_markers(false), layer_count(0), - layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), + layer_index(-1), print_layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), elapsed_time_bridges(0.0), elapsed_time_external(0.0), volumetric_speed(0), _last_pos_defined(false) { @@ -267,6 +267,8 @@ GCode::change_layer(const Layer &layer) { this->layer = &layer; this->layer_index++; + if (!layer.is_support()) + this->print_layer_index++; this->first_layer = (layer.id() == 0); // avoid computing islands and overhangs if they're not needed @@ -483,13 +485,32 @@ GCode::extrude(const ExtrusionPath &path, std::string description, double speed) return gcode; } +static bool is_support_path(ExtrusionPath path) +{ + return path.role == erSupportMaterial || path.role == erSupportMaterialInterface; +} + std::string GCode::_extrude(ExtrusionPath path, std::string description, double speed) { path.simplify(SCALED_RESOLUTION); std::string gcode; description = path.is_bridge() ? description + " (bridge)" : description; - + + if (path.is_infill() && !this->config.before_infill_gcode.value.empty()) { + PlaceholderParser pp = *this->placeholder_parser; + pp.set("layer_num", this->print_layer_index); + gcode += Slic3r::apply_math(pp.process(this->config.before_infill_gcode.value)) + '\n'; + } else if (path.is_perimeter() && !this->config.before_perimeter_gcode.value.empty()) { + PlaceholderParser pp = *this->placeholder_parser; + pp.set("layer_num", this->print_layer_index); + gcode += Slic3r::apply_math(pp.process(this->config.before_perimeter_gcode.value)) + '\n'; + } else if (is_support_path(path) && !this->config.before_support_gcode.value.empty()) { + PlaceholderParser pp = *this->placeholder_parser; + pp.set("layer_num", this->print_layer_index); + gcode += Slic3r::apply_math(pp.process(this->config.before_support_gcode.value)) + '\n'; + } + // go to first point of extrusion path if (!this->_last_pos_defined || !this->_last_pos.coincides_with(path.first_point())) { gcode += this->travel_to( diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 5055fd2b60..5d2596a890 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -85,6 +85,7 @@ class GCode { bool enable_cooling_markers; size_t layer_count; int layer_index; // just a counter + int print_layer_index; // just a counter const Layer* layer; std::map _seam_position; bool first_layer; // this flag triggers first layer speeds diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp index 5b536031ba..33a3e48bcb 100644 --- a/xs/src/libslic3r/GCodeSender.cpp +++ b/xs/src/libslic3r/GCodeSender.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #if defined(__APPLE__) || defined(__OpenBSD__) #include @@ -529,7 +530,7 @@ void GCodeSender::set_DTR(bool on) { #if defined(_WIN32) && !defined(__SYMBIAN32__) - asio::serial_port_service::native_handle_type handle = this->serial.native_handle(); + asio::serial_port::native_handle_type handle = this->serial.native_handle(); if (on) EscapeCommFunction(handle, SETDTR); else diff --git a/xs/src/libslic3r/GCodeSender.hpp b/xs/src/libslic3r/GCodeSender.hpp index 8f61a80b21..fbb8c49d6f 100644 --- a/xs/src/libslic3r/GCodeSender.hpp +++ b/xs/src/libslic3r/GCodeSender.hpp @@ -16,15 +16,12 @@ #include #include +using namespace boost::placeholders; + namespace Slic3r { namespace asio = boost::asio; -#if BOOST_VERSION >= 107300 -using boost::placeholders::_1; -using boost::placeholders::_2; -#endif - class GCodeSender : private boost::noncopyable { public: GCodeSender(); diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index f72b4762d4..63b85d23eb 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -5,78 +5,88 @@ #include #endif -namespace Slic3r { +using namespace boost::placeholders; -#if BOOST_VERSION >= 107300 -using boost::placeholders::_1; -using boost::placeholders::_2; -#endif - -void -GCodeTimeEstimator::parse(const std::string &gcode) +namespace Slic3r { - GCodeReader::parse(gcode, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); -} -void -GCodeTimeEstimator::parse_file(const std::string &file) -{ - GCodeReader::parse_file(file, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); -} + void + GCodeTimeEstimator::parse(const std::string &gcode) + { + GCodeReader::parse(gcode, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); + } -void -GCodeTimeEstimator::_parser(GCodeReader&, const GCodeReader::GCodeLine &line) -{ - // std::cout << "[" << this->time << "] " << line.raw << std::endl; - if (line.cmd == "G1") { - const float dist_XY = line.dist_XY(); - const float new_F = line.new_F(); - - if (dist_XY > 0) { - //this->time += dist_XY / new_F * 60; - this->time += _accelerated_move(dist_XY, new_F/60, this->acceleration); - } else { - //this->time += std::abs(line.dist_E()) / new_F * 60; - this->time += _accelerated_move(std::abs(line.dist_E()), new_F/60, this->acceleration); + void + GCodeTimeEstimator::parse_file(const std::string &file) + { + GCodeReader::parse_file(file, boost::bind(&GCodeTimeEstimator::_parser, this, _1, _2)); + } + + void + GCodeTimeEstimator::_parser(GCodeReader &, const GCodeReader::GCodeLine &line) + { + // std::cout << "[" << this->time << "] " << line.raw << std::endl; + if (line.cmd == "G1") + { + const float dist_XY = line.dist_XY(); + const float new_F = line.new_F(); + + if (dist_XY > 0) + { + // this->time += dist_XY / new_F * 60; + this->time += _accelerated_move(dist_XY, new_F / 60, this->acceleration); + } + else + { + // this->time += std::abs(line.dist_E()) / new_F * 60; + this->time += _accelerated_move(std::abs(line.dist_E()), new_F / 60, this->acceleration); + } + // this->time += std::abs(line.dist_Z()) / new_F * 60; + this->time += _accelerated_move(std::abs(line.dist_Z()), new_F / 60, this->acceleration); + } + else if (line.cmd == "M204" && line.has('S')) + { + this->acceleration = line.get_float('S'); } - //this->time += std::abs(line.dist_Z()) / new_F * 60; - this->time += _accelerated_move(std::abs(line.dist_Z()), new_F/60, this->acceleration); - } else if (line.cmd == "M204" && line.has('S')) { - this->acceleration = line.get_float('S'); - } else if (line.cmd == "G4") { // swell - if (line.has('S')) { - this->time += line.get_float('S'); - } else if (line.has('P')) { - this->time += line.get_float('P')/1000; + else if (line.cmd == "G4") + { // swell + if (line.has('S')) + { + this->time += line.get_float('S'); + } + else if (line.has('P')) + { + this->time += line.get_float('P') / 1000; + } } } -} -// Wildly optimistic acceleration "bell" curve modeling. -// Returns an estimate of how long the move with a given accel -// takes in seconds. -// It is assumed that the movement is smooth and uniform. -float -GCodeTimeEstimator::_accelerated_move(double length, double v, double acceleration) -{ - // for half of the move, there are 2 zones, where the speed is increasing/decreasing and - // where the speed is constant. - // Since the slowdown is assumed to be uniform, calculate the average velocity for half of the - // expected displacement. - // final velocity v = a*t => a * (dx / 0.5v) => v^2 = 2*a*dx - // v_avg = 0.5v => 2*v_avg = v - // d_x = v_avg*t => t = d_x / v_avg - acceleration = (acceleration == 0.0 ? 4000.0 : acceleration); // Set a default accel to use for print time in case it's 0 somehow. - auto half_length = length / 2.0; - auto t_init = v / acceleration; // time to final velocity - auto dx_init = (0.5*v*t_init); // Initial displacement for the time to get to final velocity - auto t = 0.0; - if (half_length >= dx_init) { - half_length -= (0.5*v*t_init); - t += t_init; + // Wildly optimistic acceleration "bell" curve modeling. + // Returns an estimate of how long the move with a given accel + // takes in seconds. + // It is assumed that the movement is smooth and uniform. + float + GCodeTimeEstimator::_accelerated_move(double length, double v, double acceleration) + { + // for half of the move, there are 2 zones, where the speed is increasing/decreasing and + // where the speed is constant. + // Since the slowdown is assumed to be uniform, calculate the average velocity for half of the + // expected displacement. + // final velocity v = a*t => a * (dx / 0.5v) => v^2 = 2*a*dx + // v_avg = 0.5v => 2*v_avg = v + // d_x = v_avg*t => t = d_x / v_avg + acceleration = (acceleration == 0.0 ? 4000.0 : acceleration); // Set a default accel to use for print time in case it's 0 somehow. + auto half_length = length / 2.0; + auto t_init = v / acceleration; // time to final velocity + auto dx_init = (0.5 * v * t_init); // Initial displacement for the time to get to final velocity + auto t = 0.0; + if (half_length >= dx_init) + { + half_length -= (0.5 * v * t_init); + t += t_init; + } + t += (half_length / v); // constant speed for rest of the time and too short displacements + return 2.0 * t; // cut in half before, so double to get full time spent. } - t += (half_length / v); // constant speed for rest of the time and too short displacements - return 2.0*t; // cut in half before, so double to get full time spent. -} } diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 635e2362ba..4008057481 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -198,7 +198,8 @@ LayerRegion::make_fill() f->bounding_box = this->layer()->object()->bounding_box(); // calculate the actual flow we'll be using for this infill - coordf_t h = (surface.thickness == -1) ? this->layer()->height : surface.thickness; + coordf_t h = (surface.thickness != -1) ? surface.thickness : + (this->layer()->id() == 0 && this->layer()->upper_layer) ? this->layer()->upper_layer->height : this->layer()->height; Flow flow = this->region()->flow( role, h, diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 72413ebb80..4753f97c82 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -9,18 +9,13 @@ #include #include #include +#include #include #include #include #include #include -#ifdef __cpp_lib_quoted_string_io - #include -#else - #include -#endif - namespace Slic3r { template @@ -724,17 +719,13 @@ Print::export_gcode(std::string outfile, bool quiet) this->status_cb(95, "Running post-processing scripts..."); this->config.setenv_(); - for (std::string ppscript : this->config.post_process.values) { - #ifdef __cpp_lib_quoted_string_io - std::stringstream _tmp_string(ppscript); - _tmp_string << " " << std::quoted(outfile); - ppscript = _tmp_string.str(); - #else - boost::replace_all(ppscript, "\"", "\\\""); - ppscript += " \"" + outfile + "\""; - #endif + for (std::string ppscript : this->config.post_process.values) + { + + boost::replace_all(ppscript, "\"", "\\\""); + ppscript += " \"" + outfile + "\""; system(ppscript.c_str()); - + // TODO: system() should be only used if user enabled an option for explicitly // supporting arguments, otherwise we should use exec*() and call the executable // directly without launching a shell. #4000 diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index f463c34413..6292e94f1c 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -77,6 +77,13 @@ PrintConfigDef::PrintConfigDef() def->max = 300; def->default_value = new ConfigOptionInt(0); + def = this->add("before_infill_gcode", coString); + def->label = __TRANS("Before infill G-code"); + def->category = __TRANS("Infill"); + def->tooltip = __TRANS("This custom code is inserted before infill."); + def->cli = "before-infill-gcode=s"; + def->default_value = new ConfigOptionString(""); + def = this->add("before_layer_gcode", coString); def->label = __TRANS("Before layer change G-code"); def->tooltip = __TRANS("This custom code is inserted at every layer change, right before the Z move. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num], [layer_z] and [current_retraction]."); @@ -86,6 +93,13 @@ PrintConfigDef::PrintConfigDef() def->height = 50; def->default_value = new ConfigOptionString(""); + def = this->add("before_perimeter_gcode", coString); + def->label = __TRANS("Before perimeter G-code"); + def->category = __TRANS("Layers and Perimeters"); + def->tooltip = __TRANS("This custom code is inserted before perimeter."); + def->cli = "before-perimeter-gcode=s"; + def->default_value = new ConfigOptionString(""); + def = this->add("between_objects_gcode", coString); def->label = __TRANS("Between objects G-code"); def->tooltip = __TRANS("This code is inserted between objects when using sequential printing. By default extruder and bed temperature are reset using non-wait command; however if M104, M109, M140 or M190 are detected in this custom code, Slic3r will not add temperature commands. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."); @@ -95,6 +109,13 @@ PrintConfigDef::PrintConfigDef() def->height = 120; def->default_value = new ConfigOptionString(""); + def = this->add("before_support_gcode", coString); + def->label = __TRANS("Before support G-code"); + def->category = __TRANS("Support material"); + def->tooltip = __TRANS("This custom code is inserted before support."); + def->cli = "before-support-gcode=s"; + def->default_value = new ConfigOptionString(""); + def = this->add("bottom_infill_pattern", external_fill_pattern); def->label = __TRANS("Bottom"); def->full_label = __TRANS("Bottom infill pattern"); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 9597814845..bf67e04f9a 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -325,7 +325,10 @@ class PrintRegionConfig : public virtual StaticPrintConfig class GCodeConfig : public virtual StaticPrintConfig { public: + ConfigOptionString before_infill_gcode; ConfigOptionString before_layer_gcode; + ConfigOptionString before_perimeter_gcode; + ConfigOptionString before_support_gcode; ConfigOptionString between_objects_gcode; ConfigOptionString end_gcode; ConfigOptionStrings end_filament_gcode; @@ -370,7 +373,10 @@ class GCodeConfig : public virtual StaticPrintConfig } virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { + OPT_PTR(before_infill_gcode); OPT_PTR(before_layer_gcode); + OPT_PTR(before_perimeter_gcode); + OPT_PTR(before_support_gcode); OPT_PTR(between_objects_gcode); OPT_PTR(end_gcode); OPT_PTR(end_filament_gcode); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 23a49dceed..4e3c0d0e03 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -13,11 +13,9 @@ #include #include -namespace Slic3r { +using namespace boost::placeholders; -#if BOOST_VERSION >= 107300 -using boost::placeholders::_1; -#endif +namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : layer_height_spline(model_object->layer_height_spline), @@ -1192,13 +1190,12 @@ PrintObject::make_perimeters() } } } - - parallelize( - std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue boost::bind(&Slic3r::Layer::make_perimeters, _1), - this->_print->config.threads.value - ); - + this->_print->config.threads.value); + /* simplify slices (both layer and region slices), we only need the max resolution for perimeters @@ -1217,13 +1214,12 @@ PrintObject::infill() // prerequisites this->prepare_infill(); - - parallelize( - std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue boost::bind(&Slic3r::Layer::make_fills, _1), - this->_print->config.threads.value - ); - + this->_print->config.threads.value); + /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; */ diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index e5f837d1a6..606e6c1169 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -12,340 +12,372 @@ #include #endif -namespace Slic3r { +using namespace boost::placeholders; -#if BOOST_VERSION >= 107300 -using boost::placeholders::_1; -#endif - -void -SLAPrint::slice() +namespace Slic3r { - TriangleMesh mesh = this->model->mesh(); - mesh.repair(); - - mesh.align_to_bed(); - - // align to origin taking raft into account - this->bb = mesh.bounding_box(); - if (this->config.raft_layers > 0) { - this->bb.min.x -= this->config.raft_offset.value; - this->bb.min.y -= this->config.raft_offset.value; - this->bb.max.x += this->config.raft_offset.value; - this->bb.max.y += this->config.raft_offset.value; - } - - // if we are generating a raft, first_layer_height will not affect mesh slicing - const float lh = this->config.layer_height.value; - const float first_lh = this->config.first_layer_height.value; - - // generate the list of Z coordinates for mesh slicing - // (we slice each layer at half of its thickness) - this->layers.clear(); - { - const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh; - this->layers.push_back(Layer(first_slice_lh/2, first_slice_lh)); - } - while (this->layers.back().print_z + lh/2 <= mesh.stl.stats.max.z) { - this->layers.push_back(Layer(this->layers.back().print_z + lh/2, this->layers.back().print_z + lh)); - } - - // perform slicing and generate layers + + void + SLAPrint::slice() { - std::vector slice_z; - for (size_t i = 0; i < this->layers.size(); ++i) - slice_z.push_back(this->layers[i].slice_z); - - std::vector slices; - TriangleMeshSlicer(&mesh).slice(slice_z, &slices); - - for (size_t i = 0; i < slices.size(); ++i) - this->layers[i].slices.expolygons = slices[i]; - } - - // generate infill - if (this->config.fill_density < 100) { - std::unique_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); - fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); - fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); - fill->min_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); - fill->angle = Geometry::deg2rad(this->config.fill_angle.value); - fill->density = this->config.fill_density.value/100; - - // Minimum spacing has a lower bound of > 0. Set to a sane default - // if the user gets an invalid value here. - fill->min_spacing = (fill->min_spacing <= 0 ? 0.5 : fill->min_spacing); - - parallelize( - 0, - this->layers.size()-1, - boost::bind(&SLAPrint::_infill_layer, this, _1, fill.get()), - this->config.threads.value - ); - } - - // generate support material - this->sm_pillars.clear(); - ExPolygons overhangs; - if (this->config.support_material) { - // flatten and merge all the overhangs + TriangleMesh mesh = this->model->mesh(); + mesh.repair(); + + mesh.align_to_bed(); + + // align to origin taking raft into account + this->bb = mesh.bounding_box(); + if (this->config.raft_layers > 0) { - Polygons pp; - for (std::vector::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) - pp += diff(it->slices, (it - 1)->slices); - overhangs = union_ex(pp); + this->bb.min.x -= this->config.raft_offset.value; + this->bb.min.y -= this->config.raft_offset.value; + this->bb.max.x += this->config.raft_offset.value; + this->bb.max.y += this->config.raft_offset.value; } - - // generate points following the shape of each island - Points pillars_pos; - const coordf_t spacing = scale_(this->config.support_material_spacing); - const coordf_t radius = scale_(this->sm_pillars_radius()); - for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { - // leave a radius/2 gap between pillars and contour to prevent lateral adhesion - for (float inset = radius * 1.5;; inset += spacing) { - // inset according to the configured spacing - Polygons curr = offset(*it, -inset); - if (curr.empty()) break; - - // generate points along the contours - for (Polygons::const_iterator pg = curr.begin(); pg != curr.end(); ++pg) { - Points pp = pg->equally_spaced_points(spacing); - for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) - pillars_pos.push_back(*p); + + // if we are generating a raft, first_layer_height will not affect mesh slicing + const float lh = this->config.layer_height.value; + const float first_lh = this->config.first_layer_height.value; + + // generate the list of Z coordinates for mesh slicing + // (we slice each layer at half of its thickness) + this->layers.clear(); + { + const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh; + this->layers.push_back(Layer(first_slice_lh / 2, first_slice_lh)); + } + while (this->layers.back().print_z + lh / 2 <= mesh.stl.stats.max.z) + { + this->layers.push_back(Layer(this->layers.back().print_z + lh / 2, this->layers.back().print_z + lh)); + } + + // perform slicing and generate layers + { + std::vector slice_z; + for (size_t i = 0; i < this->layers.size(); ++i) + slice_z.push_back(this->layers[i].slice_z); + + std::vector slices; + TriangleMeshSlicer(&mesh).slice(slice_z, &slices); + + for (size_t i = 0; i < slices.size(); ++i) + this->layers[i].slices.expolygons = slices[i]; + } + + // generate infill + if (this->config.fill_density < 100) + { + std::unique_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); + fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); + fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); + fill->min_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + fill->angle = Geometry::deg2rad(this->config.fill_angle.value); + fill->density = this->config.fill_density.value / 100; + + // Minimum spacing has a lower bound of > 0. Set to a sane default + // if the user gets an invalid value here. + fill->min_spacing = (fill->min_spacing <= 0 ? 0.5 : fill->min_spacing); + + parallelize( + 0, + this->layers.size() - 1, + boost::bind(&SLAPrint::_infill_layer, this, _1, fill.get()), + this->config.threads.value); + } + + // generate support material + this->sm_pillars.clear(); + ExPolygons overhangs; + if (this->config.support_material) + { + // flatten and merge all the overhangs + { + Polygons pp; + for (std::vector::const_iterator it = this->layers.begin() + 1; it != this->layers.end(); ++it) + pp += diff(it->slices, (it - 1)->slices); + overhangs = union_ex(pp); + } + + // generate points following the shape of each island + Points pillars_pos; + const coordf_t spacing = scale_(this->config.support_material_spacing); + const coordf_t radius = scale_(this->sm_pillars_radius()); + for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) + { + // leave a radius/2 gap between pillars and contour to prevent lateral adhesion + for (float inset = radius * 1.5;; inset += spacing) + { + // inset according to the configured spacing + Polygons curr = offset(*it, -inset); + if (curr.empty()) + break; + + // generate points along the contours + for (Polygons::const_iterator pg = curr.begin(); pg != curr.end(); ++pg) + { + Points pp = pg->equally_spaced_points(spacing); + for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) + pillars_pos.push_back(*p); + } } } - } - - // for each pillar, check which layers it applies to - for (Points::const_iterator p = pillars_pos.begin(); p != pillars_pos.end(); ++p) { - SupportPillar pillar(*p); - bool object_hit = false; - - // check layers top-down - for (int i = this->layers.size()-1; i >= 0; --i) { - // check whether point is void in this layer - if (!this->layers[i].slices.contains(*p)) { - // no slice contains the point, so it's in the void - if (pillar.top_layer > 0) { - // we have a pillar, so extend it - pillar.bottom_layer = i + this->config.raft_layers; - } else if (object_hit) { - // we don't have a pillar and we're below the object, so create one - pillar.top_layer = i + this->config.raft_layers; + + // for each pillar, check which layers it applies to + for (Points::const_iterator p = pillars_pos.begin(); p != pillars_pos.end(); ++p) + { + SupportPillar pillar(*p); + bool object_hit = false; + + // check layers top-down + for (int i = this->layers.size() - 1; i >= 0; --i) + { + // check whether point is void in this layer + if (!this->layers[i].slices.contains(*p)) + { + // no slice contains the point, so it's in the void + if (pillar.top_layer > 0) + { + // we have a pillar, so extend it + pillar.bottom_layer = i + this->config.raft_layers; + } + else if (object_hit) + { + // we don't have a pillar and we're below the object, so create one + pillar.top_layer = i + this->config.raft_layers; + } } - } else { - if (pillar.top_layer > 0) { - // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar - this->sm_pillars.push_back(pillar); - pillar = SupportPillar(*p); + else + { + if (pillar.top_layer > 0) + { + // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar + this->sm_pillars.push_back(pillar); + pillar = SupportPillar(*p); + } + object_hit = true; } - object_hit = true; } + if (pillar.top_layer > 0) + this->sm_pillars.push_back(pillar); } - if (pillar.top_layer > 0) this->sm_pillars.push_back(pillar); - } - } - - // generate a solid raft if requested - // (do this after support material because we take support material shape into account) - if (this->config.raft_layers > 0) { - ExPolygons raft = this->layers.front().slices + overhangs; // take support material into account - raft = offset_ex(raft, scale_(this->config.raft_offset)); - for (int i = this->config.raft_layers; i >= 1; --i) { - this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i-1))); - this->layers.front().slices = raft; } - - // prepend total raft height to all sliced layers - for (size_t i = this->config.raft_layers; i < this->layers.size(); ++i) - this->layers[i].print_z += first_lh + lh * (this->config.raft_layers-1); - } -} -void -SLAPrint::_infill_layer(size_t i, const Fill* _fill) -{ - Layer &layer = this->layers[i]; - - const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); - - // In order to detect what regions of this layer need to be solid, - // perform an intersection with layers within the requested shell thickness. - Polygons internal = layer.slices; - for (size_t j = 0; j < this->layers.size(); ++j) { - const Layer &other = this->layers[j]; - if (std::abs(other.print_z - layer.print_z) > shell_thickness) continue; - - if (j == 0 || j == this->layers.size()-1) { - internal.clear(); - break; - } else if (i != j) { - internal = intersection(internal, other.slices); - if (internal.empty()) break; + // generate a solid raft if requested + // (do this after support material because we take support material shape into account) + if (this->config.raft_layers > 0) + { + ExPolygons raft = this->layers.front().slices + overhangs; // take support material into account + raft = offset_ex(raft, scale_(this->config.raft_offset)); + for (int i = this->config.raft_layers; i >= 1; --i) + { + this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i - 1))); + this->layers.front().slices = raft; + } + + // prepend total raft height to all sliced layers + for (size_t i = this->config.raft_layers; i < this->layers.size(); ++i) + this->layers[i].print_z += first_lh + lh * (this->config.raft_layers - 1); } } - - // If we have no internal infill, just print the whole layer as a solid slice. - if (internal.empty()) return; - layer.solid = false; - - const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); - - // Generate solid infill - layer.solid_infill << diff_ex(infill, internal, true); - - // Generate internal infill + + void + SLAPrint::_infill_layer(size_t i, const Fill *_fill) { - std::unique_ptr fill(_fill->clone()); - fill->layer_id = i; - fill->z = layer.print_z; - - ExtrusionPath templ(erInternalInfill); - - const ExPolygons internal_ex = intersection_ex(infill, internal); - for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { - Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); - templ.width = fill->spacing(); // fill->spacing doesn't have anything defined until after fill_surface - layer.infill.append(polylines, templ); + Layer &layer = this->layers[i]; + + const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); + + // In order to detect what regions of this layer need to be solid, + // perform an intersection with layers within the requested shell thickness. + Polygons internal = layer.slices; + for (size_t j = 0; j < this->layers.size(); ++j) + { + const Layer &other = this->layers[j]; + if (std::abs(other.print_z - layer.print_z) > shell_thickness) + continue; + + if (j == 0 || j == this->layers.size() - 1) + { + internal.clear(); + break; + } + else if (i != j) + { + internal = intersection(internal, other.slices); + if (internal.empty()) + break; + } } + + // If we have no internal infill, just print the whole layer as a solid slice. + if (internal.empty()) + return; + layer.solid = false; + + const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); + + // Generate solid infill + layer.solid_infill << diff_ex(infill, internal, true); + + // Generate internal infill + { + std::unique_ptr fill(_fill->clone()); + fill->layer_id = i; + fill->z = layer.print_z; + + ExtrusionPath templ(erInternalInfill); + + const ExPolygons internal_ex = intersection_ex(infill, internal); + for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) + { + Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); + templ.width = fill->spacing(); // fill->spacing doesn't have anything defined until after fill_surface + layer.infill.append(polylines, templ); + } + } + + // Generate perimeter(s). + layer.perimeters << diff_ex( + layer.slices, + offset(layer.slices, -scale_(shell_thickness))); } - - // Generate perimeter(s). - layer.perimeters << diff_ex( - layer.slices, - offset(layer.slices, -scale_(shell_thickness)) - ); -} -void -SLAPrint::write_svg(const std::string &outputfile) const -{ - const Sizef3 size = this->bb.size(); - const double support_material_radius = sm_pillars_radius(); - - FILE* f = fopen(outputfile.c_str(), "w"); - fprintf(f, - "\n" - "\n" - "\n" - "\n" - , size.x, size.y, SLIC3R_VERSION); - - for (size_t i = 0; i < this->layers.size(); ++i) { - const Layer &layer = this->layers[i]; + void + SLAPrint::write_svg(const std::string &outputfile) const + { + const Sizef3 size = this->bb.size(); + const double support_material_radius = sm_pillars_radius(); + + FILE *f = fopen(outputfile.c_str(), "w"); fprintf(f, - "\t\n", - i, - layer.print_z, - layer.slice_z, - layer.print_z - ((i == 0) ? 0. : this->layers[i-1].print_z) - ); - - if (layer.solid) { - const ExPolygons &slices = layer.slices.expolygons; - for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { - std::string pd = this->_SVG_path_d(*it); - - fprintf(f,"\t\t\n", - pd.c_str(), "white", "black", "0", unscale(unscale(it->area())) - ); - } - } else { - // Perimeters. - for (ExPolygons::const_iterator it = layer.perimeters.expolygons.begin(); - it != layer.perimeters.expolygons.end(); ++it) { - std::string pd = this->_SVG_path_d(*it); - - fprintf(f,"\t\t\n", - pd.c_str(), "white", "black", "0" - ); - } - - // Solid infill. - for (ExPolygons::const_iterator it = layer.solid_infill.expolygons.begin(); - it != layer.solid_infill.expolygons.end(); ++it) { - std::string pd = this->_SVG_path_d(*it); - - fprintf(f,"\t\t\n", - pd.c_str(), "white", "black", "0" - ); + "\n" + "\n" + "\n" + "\n", + size.x, size.y, SLIC3R_VERSION); + + for (size_t i = 0; i < this->layers.size(); ++i) + { + const Layer &layer = this->layers[i]; + fprintf(f, + "\t\n", + i, + layer.print_z, + layer.slice_z, + layer.print_z - ((i == 0) ? 0. : this->layers[i - 1].print_z)); + + if (layer.solid) + { + const ExPolygons &slices = layer.slices.expolygons; + for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) + { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f, "\t\t\n", + pd.c_str(), "white", "black", "0", unscale(unscale(it->area()))); + } } - - // Internal infill. - for (ExtrusionEntitiesPtr::const_iterator it = layer.infill.entities.begin(); - it != layer.infill.entities.end(); ++it) { - const ExPolygons infill = union_ex((*it)->grow()); - - for (ExPolygons::const_iterator e = infill.begin(); e != infill.end(); ++e) { - std::string pd = this->_SVG_path_d(*e); - - fprintf(f,"\t\t\n", - pd.c_str(), "white", "black", "0" - ); + else + { + // Perimeters. + for (ExPolygons::const_iterator it = layer.perimeters.expolygons.begin(); + it != layer.perimeters.expolygons.end(); ++it) + { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f, "\t\t\n", + pd.c_str(), "white", "black", "0"); + } + + // Solid infill. + for (ExPolygons::const_iterator it = layer.solid_infill.expolygons.begin(); + it != layer.solid_infill.expolygons.end(); ++it) + { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f, "\t\t\n", + pd.c_str(), "white", "black", "0"); + } + + // Internal infill. + for (ExtrusionEntitiesPtr::const_iterator it = layer.infill.entities.begin(); + it != layer.infill.entities.end(); ++it) + { + const ExPolygons infill = union_ex((*it)->grow()); + + for (ExPolygons::const_iterator e = infill.begin(); e != infill.end(); ++e) + { + std::string pd = this->_SVG_path_d(*e); + + fprintf(f, "\t\t\n", + pd.c_str(), "white", "black", "0"); + } } } - } - - // don't print support material in raft layers - if (i >= (size_t)this->config.raft_layers) { - // look for support material pillars belonging to this layer - for (std::vector::const_iterator it = this->sm_pillars.begin(); it != this->sm_pillars.end(); ++it) { - if (!(it->top_layer >= i && it->bottom_layer <= i)) continue; - - // generate a conic tip - float radius = fminf( - support_material_radius, - (it->top_layer - i + 1) * this->config.layer_height.value - ); - - fprintf(f,"\t\t\n", - unscale(it->x) - this->bb.min.x, - size.y - (unscale(it->y) - this->bb.min.y), - radius - ); + + // don't print support material in raft layers + if (i >= (size_t)this->config.raft_layers) + { + // look for support material pillars belonging to this layer + for (std::vector::const_iterator it = this->sm_pillars.begin(); it != this->sm_pillars.end(); ++it) + { + if (!(it->top_layer >= i && it->bottom_layer <= i)) + continue; + + // generate a conic tip + float radius = fminf( + support_material_radius, + (it->top_layer - i + 1) * this->config.layer_height.value); + + fprintf(f, "\t\t\n", + unscale(it->x) - this->bb.min.x, + size.y - (unscale(it->y) - this->bb.min.y), + radius); + } } + + fprintf(f, "\t\n"); } - - fprintf(f,"\t\n"); - } - fprintf(f,"\n"); - // Ensure that the output gets written. - fflush(f); + fprintf(f, "\n"); + // Ensure that the output gets written. + fflush(f); - // Close the file. - fclose(f); -} + // Close the file. + fclose(f); + } -coordf_t -SLAPrint::sm_pillars_radius() const -{ - coordf_t radius = this->config.support_material_extrusion_width.get_abs_value(this->config.support_material_spacing)/2; - if (radius == 0) radius = this->config.support_material_spacing / 3; // auto - return radius; -} + coordf_t + SLAPrint::sm_pillars_radius() const + { + coordf_t radius = this->config.support_material_extrusion_width.get_abs_value(this->config.support_material_spacing) / 2; + if (radius == 0) + radius = this->config.support_material_spacing / 3; // auto + return radius; + } -std::string -SLAPrint::_SVG_path_d(const Polygon &polygon) const -{ - const Sizef3 size = this->bb.size(); - std::ostringstream d; - d << "M "; - for (Points::const_iterator p = polygon.points.begin(); p != polygon.points.end(); ++p) { - d << unscale(p->x) - this->bb.min.x << " "; - d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y + std::string + SLAPrint::_SVG_path_d(const Polygon &polygon) const + { + const Sizef3 size = this->bb.size(); + std::ostringstream d; + d << "M "; + for (Points::const_iterator p = polygon.points.begin(); p != polygon.points.end(); ++p) + { + d << unscale(p->x) - this->bb.min.x << " "; + d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y + } + d << "z"; + return d.str(); } - d << "z"; - return d.str(); -} -std::string -SLAPrint::_SVG_path_d(const ExPolygon &expolygon) const -{ - std::string pd; - const Polygons pp = expolygon; - for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) - pd += this->_SVG_path_d(*mp) + " "; - return pd; -} + std::string + SLAPrint::_SVG_path_d(const ExPolygon &expolygon) const + { + std::string pd; + const Polygons pp = expolygon; + for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) + pd += this->_SVG_path_d(*mp) + " "; + return pd; + } } diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index ce9ac952c1..4c6e32de61 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -1,6 +1,7 @@ #include "SupportMaterial.hpp" #include "Log.hpp" +using namespace boost::placeholders; namespace Slic3r { @@ -1191,8 +1192,9 @@ SupportMaterial::p(SurfacesPtr &surfaces) void SupportMaterial::append_polygons(Polygons &dst, Polygons &src) { - for (const auto polygon : src) { - dst.push_back(polygon); + for (const auto &polygon : src) + { + dst.emplace_back(polygon); } } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 4e62bdaf25..dc08e0b44a 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -6,7 +6,12 @@ #include #include #include - +#include +#if BOOST_VERSION >= 107300 +#include +#else +#include +#endif #include "libslic3r.h" #include "ClipperUtils.hpp" @@ -21,6 +26,7 @@ #include "SVG.hpp" using namespace std; +using namespace boost::placeholders; namespace Slic3r { diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c13802c1d6..a1c39d636b 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -24,12 +24,9 @@ #include "SVG.hpp" #endif -namespace Slic3r { - +using namespace boost::placeholders; -#if BOOST_VERSION >= 107300 -using boost::placeholders::_1; -#endif +namespace Slic3r { TriangleMesh::TriangleMesh() : repaired(false)