summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2018-06-30 12:43:08 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2018-06-30 12:43:08 +0200
commita98326eb2954ac1e79f5eac28dbeab3ec15e047f (patch)
treebb16257a1894b488e365851273735ec13a9442ef
parent10.0 (diff)
downloadFreeFileSync-a98326eb2954ac1e79f5eac28dbeab3ec15e047f.tar.gz
FreeFileSync-a98326eb2954ac1e79f5eac28dbeab3ec15e047f.tar.bz2
FreeFileSync-a98326eb2954ac1e79f5eac28dbeab3ec15e047f.zip
10.1
-rwxr-xr-xChangelog.txt24
-rwxr-xr-xFreeFileSync/Build/Help/html/performance.html13
-rwxr-xr-xFreeFileSync/Build/Help/html/realtimesync.html4
-rwxr-xr-xFreeFileSync/Build/Help/images/basic-step-compare.pngbin2823 -> 2711 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/command-line-syntax.pngbin11836 -> 8021 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/comparison-settings.pngbin23375 -> 16588 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/comparison-variant-double-click.pngbin5818 -> 3916 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/copy-alternative-path.pngbin2749 -> 2637 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/filter.pngbin25733 -> 19163 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/gnome-scheduler.pngbin49329 -> 44019 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/ignore-time-shift.pngbin16368 -> 11862 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/performance.pngbin5457 -> 3454 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/realtimesync-create-shortcut.pngbin6353 -> 6240 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/realtimesync-schedule.pngbin12688 -> 8443 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/save-automator.pngbin9447 -> 9381 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/setup-batch-job.pngbin19809 -> 14074 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/sftp-cloud-picker.pngbin689 -> 495 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/sftp-login.pngbin11307 -> 7534 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/sftp-performance.pngbin7369 -> 4532 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/synchronization-settings.pngbin36070 -> 29684 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/synchronization-variant-double-click.pngbin6422 -> 4072 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/windows-scheduler.pngbin13628 -> 8897 bytes
-rwxr-xr-xFreeFileSync/Build/Help/images/xp-scheduler.pngbin20957 -> 17907 bytes
-rwxr-xr-xFreeFileSync/Build/Languages/german.lng20
-rwxr-xr-xFreeFileSync/Build/Resources.zipbin319981 -> 318268 bytes
-rwxr-xr-xFreeFileSync/Source/Makefile49
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/Makefile11
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/application.cpp15
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/application.h2
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/folder_selector2.cpp2
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/main_dlg.cpp6
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/monitor.cpp4
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/tray_menu.cpp2
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/xml_proc.cpp4
-rwxr-xr-xFreeFileSync/Source/base/algorithm.cpp (renamed from FreeFileSync/Source/algorithm.cpp)79
-rwxr-xr-xFreeFileSync/Source/base/algorithm.h (renamed from FreeFileSync/Source/algorithm.h)4
-rwxr-xr-xFreeFileSync/Source/base/application.cpp (renamed from FreeFileSync/Source/application.cpp)16
-rwxr-xr-xFreeFileSync/Source/base/application.h (renamed from FreeFileSync/Source/application.h)4
-rwxr-xr-xFreeFileSync/Source/base/binary.cpp (renamed from FreeFileSync/Source/lib/binary.cpp)6
-rwxr-xr-xFreeFileSync/Source/base/binary.h (renamed from FreeFileSync/Source/lib/binary.h)0
-rwxr-xr-xFreeFileSync/Source/base/cmp_filetime.h (renamed from FreeFileSync/Source/lib/cmp_filetime.h)0
-rwxr-xr-xFreeFileSync/Source/base/comparison.cpp (renamed from FreeFileSync/Source/comparison.cpp)382
-rwxr-xr-xFreeFileSync/Source/base/comparison.h (renamed from FreeFileSync/Source/comparison.h)6
-rwxr-xr-xFreeFileSync/Source/base/db_file.cpp (renamed from FreeFileSync/Source/lib/db_file.cpp)6
-rwxr-xr-xFreeFileSync/Source/base/db_file.h (renamed from FreeFileSync/Source/lib/db_file.h)2
-rwxr-xr-xFreeFileSync/Source/base/dir_exist_async.h (renamed from FreeFileSync/Source/lib/dir_exist_async.h)20
-rwxr-xr-xFreeFileSync/Source/base/dir_lock.cpp (renamed from FreeFileSync/Source/lib/dir_lock.cpp)32
-rwxr-xr-xFreeFileSync/Source/base/dir_lock.h (renamed from FreeFileSync/Source/lib/dir_lock.h)0
-rwxr-xr-xFreeFileSync/Source/base/error_log.h (renamed from FreeFileSync/Source/lib/error_log.h)0
-rwxr-xr-xFreeFileSync/Source/base/ffs_paths.cpp (renamed from FreeFileSync/Source/lib/ffs_paths.cpp)0
-rwxr-xr-xFreeFileSync/Source/base/ffs_paths.h (renamed from FreeFileSync/Source/lib/ffs_paths.h)0
-rwxr-xr-xFreeFileSync/Source/base/file_hierarchy.cpp (renamed from FreeFileSync/Source/file_hierarchy.cpp)4
-rwxr-xr-xFreeFileSync/Source/base/file_hierarchy.h (renamed from FreeFileSync/Source/file_hierarchy.h)89
-rwxr-xr-xFreeFileSync/Source/base/generate_logfile.cpp (renamed from FreeFileSync/Source/lib/generate_logfile.cpp)3
-rwxr-xr-xFreeFileSync/Source/base/generate_logfile.h (renamed from FreeFileSync/Source/lib/generate_logfile.h)2
-rwxr-xr-xFreeFileSync/Source/base/hard_filter.cpp (renamed from FreeFileSync/Source/lib/hard_filter.cpp)2
-rwxr-xr-xFreeFileSync/Source/base/hard_filter.h (renamed from FreeFileSync/Source/lib/hard_filter.h)0
-rwxr-xr-xFreeFileSync/Source/base/help_provider.h (renamed from FreeFileSync/Source/lib/help_provider.h)4
-rwxr-xr-xFreeFileSync/Source/base/icon_buffer.cpp (renamed from FreeFileSync/Source/lib/icon_buffer.cpp)49
-rwxr-xr-xFreeFileSync/Source/base/icon_buffer.h (renamed from FreeFileSync/Source/lib/icon_buffer.h)6
-rwxr-xr-xFreeFileSync/Source/base/icon_loader.cpp (renamed from FreeFileSync/Source/lib/icon_loader.cpp)0
-rwxr-xr-xFreeFileSync/Source/base/icon_loader.h (renamed from FreeFileSync/Source/lib/icon_loader.h)5
-rwxr-xr-xFreeFileSync/Source/base/localization.cpp (renamed from FreeFileSync/Source/lib/localization.cpp)0
-rwxr-xr-xFreeFileSync/Source/base/localization.h (renamed from FreeFileSync/Source/lib/localization.h)0
-rwxr-xr-xFreeFileSync/Source/base/lock_holder.h (renamed from FreeFileSync/Source/lib/lock_holder.h)2
-rwxr-xr-xFreeFileSync/Source/base/norm_filter.h (renamed from FreeFileSync/Source/lib/norm_filter.h)0
-rwxr-xr-xFreeFileSync/Source/base/parallel_scan.cpp (renamed from FreeFileSync/Source/lib/parallel_scan.cpp)77
-rwxr-xr-xFreeFileSync/Source/base/parallel_scan.h (renamed from FreeFileSync/Source/lib/parallel_scan.h)6
-rwxr-xr-xFreeFileSync/Source/base/parse_lng.h (renamed from FreeFileSync/Source/lib/parse_lng.h)0
-rwxr-xr-xFreeFileSync/Source/base/parse_plural.h (renamed from FreeFileSync/Source/lib/parse_plural.h)0
-rwxr-xr-xFreeFileSync/Source/base/perf_check.cpp (renamed from FreeFileSync/Source/lib/perf_check.cpp)3
-rwxr-xr-xFreeFileSync/Source/base/perf_check.h (renamed from FreeFileSync/Source/lib/perf_check.h)0
-rwxr-xr-xFreeFileSync/Source/base/process_callback.h (renamed from FreeFileSync/Source/process_callback.h)0
-rwxr-xr-xFreeFileSync/Source/base/process_xml.cpp (renamed from FreeFileSync/Source/lib/process_xml.cpp)0
-rwxr-xr-xFreeFileSync/Source/base/process_xml.h (renamed from FreeFileSync/Source/lib/process_xml.h)4
-rwxr-xr-xFreeFileSync/Source/base/resolve_path.cpp (renamed from FreeFileSync/Source/lib/resolve_path.cpp)6
-rwxr-xr-xFreeFileSync/Source/base/resolve_path.h (renamed from FreeFileSync/Source/lib/resolve_path.h)0
-rwxr-xr-xFreeFileSync/Source/base/return_codes.h (renamed from FreeFileSync/Source/lib/return_codes.h)0
-rwxr-xr-xFreeFileSync/Source/base/soft_filter.h (renamed from FreeFileSync/Source/lib/soft_filter.h)2
-rwxr-xr-xFreeFileSync/Source/base/status_handler.cpp (renamed from FreeFileSync/Source/lib/status_handler.cpp)0
-rwxr-xr-xFreeFileSync/Source/base/status_handler.h (renamed from FreeFileSync/Source/lib/status_handler.h)7
-rwxr-xr-xFreeFileSync/Source/base/status_handler_impl.h386
-rwxr-xr-xFreeFileSync/Source/base/structures.cpp (renamed from FreeFileSync/Source/structures.cpp)4
-rwxr-xr-xFreeFileSync/Source/base/structures.h (renamed from FreeFileSync/Source/structures.h)2
-rwxr-xr-xFreeFileSync/Source/base/synchronization.cpp (renamed from FreeFileSync/Source/synchronization.cpp)623
-rwxr-xr-xFreeFileSync/Source/base/synchronization.h (renamed from FreeFileSync/Source/synchronization.h)2
-rwxr-xr-xFreeFileSync/Source/base/versioning.cpp (renamed from FreeFileSync/Source/lib/versioning.cpp)7
-rwxr-xr-xFreeFileSync/Source/base/versioning.h (renamed from FreeFileSync/Source/lib/versioning.h)4
-rwxr-xr-xFreeFileSync/Source/fs/abstract.cpp29
-rwxr-xr-xFreeFileSync/Source/fs/abstract.h224
-rw-r--r--FreeFileSync/Source/fs/concrete_impl.h211
-rwxr-xr-xFreeFileSync/Source/fs/native.cpp185
-rwxr-xr-xFreeFileSync/Source/lib/status_handler_impl.h92
-rwxr-xr-xFreeFileSync/Source/ui/batch_config.cpp4
-rwxr-xr-xFreeFileSync/Source/ui/batch_config.h2
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.cpp12
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.h6
-rwxr-xr-xFreeFileSync/Source/ui/cfg_grid.cpp16
-rwxr-xr-xFreeFileSync/Source/ui/file_grid.cpp4
-rwxr-xr-xFreeFileSync/Source/ui/file_grid.h2
-rwxr-xr-xFreeFileSync/Source/ui/file_view.cpp6
-rwxr-xr-xFreeFileSync/Source/ui/file_view.h2
-rwxr-xr-xFreeFileSync/Source/ui/folder_history_box.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/folder_pair.h4
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.cpp7
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.cpp96
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.h6
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.cpp8
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.h2
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.cpp68
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.h5
-rwxr-xr-xFreeFileSync/Source/ui/progress_indicator.cpp32
-rwxr-xr-xFreeFileSync/Source/ui/progress_indicator.h4
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.cpp28
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.h4
-rwxr-xr-xFreeFileSync/Source/ui/sorting.h22
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.cpp7
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.h2
-rwxr-xr-xFreeFileSync/Source/ui/taskbar.cpp22
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.cpp21
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.h2
-rwxr-xr-xFreeFileSync/Source/ui/version_check.cpp63
-rwxr-xr-xFreeFileSync/Source/ui/version_check.h2
-rwxr-xr-xFreeFileSync/Source/version/version.h2
-rwxr-xr-xwx+/async_task.h6
-rwxr-xr-xwx+/grid.cpp4
-rwxr-xr-xwx+/http.cpp279
-rwxr-xr-xwx+/image_holder.h10
-rwxr-xr-xwx+/image_resources.cpp129
-rwxr-xr-xwx+/image_tools.cpp2
-rwxr-xr-xwx+/zlib_wrap.h6
-rwxr-xr-xxBRZ/src/xbrz.cpp8
-rwxr-xr-xzen/basic_math.h10
-rwxr-xr-xzen/build_info.h4
-rwxr-xr-xzen/crc.h9
-rwxr-xr-xzen/deprecate.h18
-rwxr-xr-xzen/dir_watcher.cpp2
-rwxr-xr-xzen/error_log.h8
-rwxr-xr-xzen/file_access.cpp16
-rwxr-xr-xzen/file_io.cpp12
-rwxr-xr-xzen/file_io.h4
-rwxr-xr-xzen/fixed_list.h240
-rwxr-xr-xzen/format_unit.cpp2
-rwxr-xr-xzen/globals.h5
-rwxr-xr-xzen/guid.h12
-rwxr-xr-xzen/http.cpp376
-rwxr-xr-xzen/http.h (renamed from wx+/http.h)17
-rwxr-xr-xzen/i18n.h2
-rwxr-xr-xzen/legacy_compiler.h89
-rwxr-xr-xzen/optional.h21
-rwxr-xr-xzen/perf.h7
-rwxr-xr-xzen/ring_buffer.h230
-rwxr-xr-xzen/scope_guard.h32
-rwxr-xr-xzen/serialize.h31
-rwxr-xr-xzen/socket.h84
-rwxr-xr-xzen/stl_tools.h34
-rwxr-xr-xzen/string_base.h24
-rwxr-xr-xzen/string_tools.h95
-rwxr-xr-xzen/string_traits.h83
-rwxr-xr-xzen/sys_error.h1
-rwxr-xr-xzen/thread.cpp55
-rwxr-xr-xzen/thread.h154
-rwxr-xr-xzen/time.h35
-rwxr-xr-xzen/type_tools.h103
-rwxr-xr-xzen/type_traits.h135
-rwxr-xr-xzen/utf.h28
-rwxr-xr-xzen/warn_static.h10
-rwxr-xr-xzen/zstring.cpp4
-rwxr-xr-xzen/zstring.h7
-rwxr-xr-xzenXml/zenxml/cvrt_struc.h50
-rwxr-xr-xzenXml/zenxml/cvrt_text.h10
-rwxr-xr-xzenXml/zenxml/dom.h15
172 files changed, 2904 insertions, 2830 deletions
diff --git a/Changelog.txt b/Changelog.txt
index 44d49196..54ba2ebd 100755
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,5 +1,23 @@
-FreeFileSync 10.0
------------------
+FreeFileSync 10.1 [2018-06-03]
+------------------------------
+Binary-compare multiple files in parallel
+Copy file permissions when creating base folders
+Fixed hang when scrolling file list (Windows)
+Fixed file list mismatch when cancelling sync
+Fixed delay when cancelling folder existence check
+Fixed comparison and sync processing order to honor FIFO
+Fixed startup delay when internet is offline (Linux, macOS)
+Fixed crash when closing FreeFileSync via the macOS Dock
+Support installation without admin rights (macOS)
+Fixed bcrypt.dll not found on startup (Windows XP)
+Respect Content-Length header for HTTP requests
+Support parallel folder traversal on Ubuntu 16.4
+Fixed missing shared library dependencies (Linux)
+Unified precompiled Linux binary packages
+
+
+FreeFileSync 10.0 [2018-04-27]
+------------------------------
The installer is now ad-free!
Sync multiple files in parallel (Donation Edition)
Compare multiple files in parallel within a single folder tree
@@ -640,7 +658,7 @@ Keep user interface responsive while creating a volume shadow copy
Fixed error when starting asynchronously from a batch script
Show progress of writing log files
Fixed updated file being left deleted when copying permissions failed
-New Project website: http://www.freefilesync.org/
+New Project website: https://freefilesync.org/
FreeFileSync 6.10 [2014-10-01]
diff --git a/FreeFileSync/Build/Help/html/performance.html b/FreeFileSync/Build/Help/html/performance.html
index 37098ed3..69922c12 100755
--- a/FreeFileSync/Build/Help/html/performance.html
+++ b/FreeFileSync/Build/Help/html/performance.html
@@ -16,18 +16,19 @@
cases where single I/O operations have significant latency
(e.g. long response times on a slow network connection)
or they cannot use the full bandwidth available
- (e.g. a SFTP server that has a speed limit for each connection).<br>
+ (e.g. an SFTP server that has a speed limit for each connection).<br>
<br>
The number of parallel file operations that FreeFileSync should use
can be set up for each device individually
in the <b>Comparison Settings</b> dialog.
- It is considered for all folder pairs of a configuration as follows:
+ It is evaluated for all folder pairs of a configuration as follows:
</p>
<ul>
- <li><b>During comparison</b> FreeFileSync first groups all folders by their root devices.<br>
+ <li><b>During comparison</b> FreeFileSync groups all folders by their root devices.<br>
<div class="half-line">&nbsp;</div>
For example, consider a configuration with two folder pairs and parallel file operations set up:
- <div>
+
+ <div style="background-color: #eee; border: 1px solid #ccc; padding: 5px 10px; margin: .5em 0;">
<table style="border-spacing:0; display: inline-block; vertical-align: middle;">
<tr><td><span class="file-path">C:\Source </span></td> <td>&harr;</td> <td><span class="file-path">D:\Target</span></td></tr>
<tr><td><span class="file-path">C:\Source2</span></td> <td>&harr;</td> <td><span class="file-path">E:\Target</span></td></tr>
@@ -42,13 +43,13 @@
<tr><td>3</td> <td><span class="file-path">E:\</span></td></tr>
</table>
</div>
- <div class="half-line">&nbsp;</div>
+
FreeFileSync will put the folders <span class="file-path">C:\Source</span> and
<span class="file-path">C:\Source2</span>
into the same group and allow only 1 file operation at a time.
Folder <span class="file-path">D:\Target</span> will be traversed using 2 operations,
and <span class="file-path">E:\Target</span> using 3 operations at a time.
- In total FreeFileSync will be scanning all folders
+ In total FreeFileSync will be scanning all four folders
employing 6 file operations in parallel.<br>
<br>
diff --git a/FreeFileSync/Build/Help/html/realtimesync.html b/FreeFileSync/Build/Help/html/realtimesync.html
index 6abbdbb2..325316b3 100755
--- a/FreeFileSync/Build/Help/html/realtimesync.html
+++ b/FreeFileSync/Build/Help/html/realtimesync.html
@@ -13,7 +13,7 @@
</h1>
<p>
- The primary purpose of RealTimeSync is to execute a command line each time it <b>detects changes</b> in one of the monitored directories,
+ The primary function of RealTimeSync is to execute a command line each time it <b>detects changes</b> in one of the monitored directories,
or when a <b>directory becomes available</b> (e. g. insert of a USB-stick). Usually this command line will trigger a FreeFileSync batch job.<br>
<br>
@@ -51,7 +51,7 @@
<div class="command-line">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;C:\Program Files\FreeFileSync\RealTimeSync.exe&quot; &quot;D:\Backup Projects.ffs_batch&quot;</div>
<br>
- <li>RealTimeSync is not required to start FreeFileSync. It can also be used in other scenarios, like sending an email whenever a certain directory is modified.
+ <li>RealTimeSync does not require to start FreeFileSync. It can also be used in other scenarios, like sending an email whenever a certain directory is modified.
</ul>
</div>
<br>
diff --git a/FreeFileSync/Build/Help/images/basic-step-compare.png b/FreeFileSync/Build/Help/images/basic-step-compare.png
index 704fd4fa..8963f9fa 100755
--- a/FreeFileSync/Build/Help/images/basic-step-compare.png
+++ b/FreeFileSync/Build/Help/images/basic-step-compare.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/command-line-syntax.png b/FreeFileSync/Build/Help/images/command-line-syntax.png
index f0d9878d..ebb2d895 100755
--- a/FreeFileSync/Build/Help/images/command-line-syntax.png
+++ b/FreeFileSync/Build/Help/images/command-line-syntax.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/comparison-settings.png b/FreeFileSync/Build/Help/images/comparison-settings.png
index 5ffa2f48..242f558a 100755
--- a/FreeFileSync/Build/Help/images/comparison-settings.png
+++ b/FreeFileSync/Build/Help/images/comparison-settings.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/comparison-variant-double-click.png b/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
index 1d998755..5ad6f256 100755
--- a/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
+++ b/FreeFileSync/Build/Help/images/comparison-variant-double-click.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/copy-alternative-path.png b/FreeFileSync/Build/Help/images/copy-alternative-path.png
index bded2b56..1baeadb4 100755
--- a/FreeFileSync/Build/Help/images/copy-alternative-path.png
+++ b/FreeFileSync/Build/Help/images/copy-alternative-path.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/filter.png b/FreeFileSync/Build/Help/images/filter.png
index a4d3a0f8..33d7264c 100755
--- a/FreeFileSync/Build/Help/images/filter.png
+++ b/FreeFileSync/Build/Help/images/filter.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/gnome-scheduler.png b/FreeFileSync/Build/Help/images/gnome-scheduler.png
index 05d0f1f7..0cd5ef12 100755
--- a/FreeFileSync/Build/Help/images/gnome-scheduler.png
+++ b/FreeFileSync/Build/Help/images/gnome-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/ignore-time-shift.png b/FreeFileSync/Build/Help/images/ignore-time-shift.png
index ceadf60a..cec19888 100755
--- a/FreeFileSync/Build/Help/images/ignore-time-shift.png
+++ b/FreeFileSync/Build/Help/images/ignore-time-shift.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/performance.png b/FreeFileSync/Build/Help/images/performance.png
index 70bed081..0c189c5c 100755
--- a/FreeFileSync/Build/Help/images/performance.png
+++ b/FreeFileSync/Build/Help/images/performance.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/realtimesync-create-shortcut.png b/FreeFileSync/Build/Help/images/realtimesync-create-shortcut.png
index c0910bf7..fcf0f7f1 100755
--- a/FreeFileSync/Build/Help/images/realtimesync-create-shortcut.png
+++ b/FreeFileSync/Build/Help/images/realtimesync-create-shortcut.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/realtimesync-schedule.png b/FreeFileSync/Build/Help/images/realtimesync-schedule.png
index 34ba5b88..416e32f8 100755
--- a/FreeFileSync/Build/Help/images/realtimesync-schedule.png
+++ b/FreeFileSync/Build/Help/images/realtimesync-schedule.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/save-automator.png b/FreeFileSync/Build/Help/images/save-automator.png
index d685d8c6..738af1de 100755
--- a/FreeFileSync/Build/Help/images/save-automator.png
+++ b/FreeFileSync/Build/Help/images/save-automator.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/setup-batch-job.png b/FreeFileSync/Build/Help/images/setup-batch-job.png
index 1982a66b..cc38e85c 100755
--- a/FreeFileSync/Build/Help/images/setup-batch-job.png
+++ b/FreeFileSync/Build/Help/images/setup-batch-job.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/sftp-cloud-picker.png b/FreeFileSync/Build/Help/images/sftp-cloud-picker.png
index a2f24f72..588d102c 100755
--- a/FreeFileSync/Build/Help/images/sftp-cloud-picker.png
+++ b/FreeFileSync/Build/Help/images/sftp-cloud-picker.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/sftp-login.png b/FreeFileSync/Build/Help/images/sftp-login.png
index d28b12a9..0a3f3b7a 100755
--- a/FreeFileSync/Build/Help/images/sftp-login.png
+++ b/FreeFileSync/Build/Help/images/sftp-login.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/sftp-performance.png b/FreeFileSync/Build/Help/images/sftp-performance.png
index 6125aa5e..f982277f 100755
--- a/FreeFileSync/Build/Help/images/sftp-performance.png
+++ b/FreeFileSync/Build/Help/images/sftp-performance.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/synchronization-settings.png b/FreeFileSync/Build/Help/images/synchronization-settings.png
index 4eab9306..501b3db1 100755
--- a/FreeFileSync/Build/Help/images/synchronization-settings.png
+++ b/FreeFileSync/Build/Help/images/synchronization-settings.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png b/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
index cb6cd370..0cd13905 100755
--- a/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
+++ b/FreeFileSync/Build/Help/images/synchronization-variant-double-click.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/windows-scheduler.png b/FreeFileSync/Build/Help/images/windows-scheduler.png
index bf214a8f..14788118 100755
--- a/FreeFileSync/Build/Help/images/windows-scheduler.png
+++ b/FreeFileSync/Build/Help/images/windows-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/xp-scheduler.png b/FreeFileSync/Build/Help/images/xp-scheduler.png
index cfb74050..4d2f5907 100755
--- a/FreeFileSync/Build/Help/images/xp-scheduler.png
+++ b/FreeFileSync/Build/Help/images/xp-scheduler.png
Binary files differ
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index 2039e816..31d74476 100755
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -7,6 +7,12 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>Donation Edition</source>
+<target>Spendenversion</target>
+
+<source>Defined by context of use</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target>
@@ -887,9 +893,6 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Please select a folder on a local file system, network or an MTP device.</source>
<target>Bitte wählen Sie einen Ordner auf einem lokalen Dateisystem, Netzwerk oder MTP Gerät.</target>
-<source>Defined by context of use</source>
-<target></target>
-
<source>Requires FreeFileSync Donation Edition</source>
<target>Benötigt FreeFileSync Spendenversion</target>
@@ -1061,8 +1064,8 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Show examples</source>
<target>Beispiele zeigen</target>
-<source>Time span:</source>
-<target>Zeitspanne:</target>
+<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
+<target>Wählen Sie Filterregeln aus, um einzelne Dateien von der Synchronisation auszuschließen. Geben Sie Dateipfade relativ zum zugehörigen Ordnerpaar an.</target>
<source>File size:</source>
<target>Dateigröße:</target>
@@ -1073,8 +1076,8 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Maximum:</source>
<target>Maximum:</target>
-<source>Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair.</source>
-<target>Wählen Sie Filterregeln aus, um einzelne Dateien von der Synchronisation auszuschließen. Geben Sie Dateipfade relativ zum zugehörigen Ordnerpaar an.</target>
+<source>Time span:</source>
+<target>Zeitspanne:</target>
<source>C&lear</source>
<target>&Löschen</target>
@@ -1381,9 +1384,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Select Time Span</source>
<target>Zeitspanne auswählen</target>
-<source>FreeFileSync Donation Edition</source>
-<target>FreeFileSync Spendenversion</target>
-
<source>Highlight Configurations</source>
<target>Konfigurationen hervorheben</target>
diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip
index a1d93d01..d020de7c 100755
--- a/FreeFileSync/Build/Resources.zip
+++ b/FreeFileSync/Build/Resources.zip
Binary files differ
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index dffa2308..89983ac8 100755
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -5,8 +5,8 @@ SHAREDIR = $(DESTDIR)$(prefix)/share
APPSHAREDIR = $(SHAREDIR)/$(APPNAME)
DOCSHAREDIR = $(SHAREDIR)/doc/$(APPNAME)
-CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -I../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
--Wall -Wfatal-errors -Winit-self -Wmissing-include-dirs -Wswitch-enum -Wmain -Wnon-virtual-dtor -Wcast-align -Wshadow -Wno-deprecated-declarations \
+CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../.. -I../../zenXml -isystem../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
+-Wall -Wfatal-errors -Wmissing-include-dirs -Wswitch-enum -Wcast-align -Wshadow -Wnon-virtual-dtor \
-O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
@@ -30,15 +30,30 @@ LINKFLAGS += `pkg-config --libs unity`
endif
CPP_FILES=
-CPP_FILES+=algorithm.cpp
-CPP_FILES+=application.cpp
-CPP_FILES+=comparison.cpp
-CPP_FILES+=structures.cpp
-CPP_FILES+=synchronization.cpp
+CPP_FILES+=base/algorithm.cpp
+CPP_FILES+=base/application.cpp
+CPP_FILES+=base/binary.cpp
+CPP_FILES+=base/comparison.cpp
+CPP_FILES+=base/db_file.cpp
+CPP_FILES+=base/dir_lock.cpp
+CPP_FILES+=base/ffs_paths.cpp
+CPP_FILES+=base/file_hierarchy.cpp
+CPP_FILES+=base/generate_logfile.cpp
+CPP_FILES+=base/hard_filter.cpp
+CPP_FILES+=base/icon_buffer.cpp
+CPP_FILES+=base/icon_loader.cpp
+CPP_FILES+=base/localization.cpp
+CPP_FILES+=base/parallel_scan.cpp
+CPP_FILES+=base/process_xml.cpp
+CPP_FILES+=base/perf_check.cpp
+CPP_FILES+=base/resolve_path.cpp
+CPP_FILES+=base/status_handler.cpp
+CPP_FILES+=base/structures.cpp
+CPP_FILES+=base/synchronization.cpp
+CPP_FILES+=base/versioning.cpp
CPP_FILES+=fs/abstract.cpp
CPP_FILES+=fs/concrete.cpp
CPP_FILES+=fs/native.cpp
-CPP_FILES+=file_hierarchy.cpp
CPP_FILES+=ui/batch_config.cpp
CPP_FILES+=ui/batch_status_handler.cpp
CPP_FILES+=ui/cfg_grid.cpp
@@ -59,35 +74,21 @@ CPP_FILES+=ui/taskbar.cpp
CPP_FILES+=ui/tray_icon.cpp
CPP_FILES+=ui/triple_splitter.cpp
CPP_FILES+=ui/version_check.cpp
-CPP_FILES+=lib/binary.cpp
-CPP_FILES+=lib/db_file.cpp
-CPP_FILES+=lib/dir_lock.cpp
-CPP_FILES+=lib/ffs_paths.cpp
-CPP_FILES+=lib/generate_logfile.cpp
-CPP_FILES+=lib/hard_filter.cpp
-CPP_FILES+=lib/icon_buffer.cpp
-CPP_FILES+=lib/icon_loader.cpp
-CPP_FILES+=lib/localization.cpp
-CPP_FILES+=lib/parallel_scan.cpp
-CPP_FILES+=lib/process_xml.cpp
-CPP_FILES+=lib/resolve_path.cpp
-CPP_FILES+=lib/perf_check.cpp
-CPP_FILES+=lib/status_handler.cpp
-CPP_FILES+=lib/versioning.cpp
CPP_FILES+=../../zen/xml_io.cpp
CPP_FILES+=../../zen/recycler.cpp
CPP_FILES+=../../zen/file_access.cpp
CPP_FILES+=../../zen/file_io.cpp
CPP_FILES+=../../zen/file_traverser.cpp
+CPP_FILES+=../../zen/http.cpp
CPP_FILES+=../../zen/zstring.cpp
CPP_FILES+=../../zen/format_unit.cpp
CPP_FILES+=../../zen/process_priority.cpp
CPP_FILES+=../../zen/shutdown.cpp
+CPP_FILES+=../../zen/thread.cpp
CPP_FILES+=../../wx+/file_drop.cpp
CPP_FILES+=../../wx+/grid.cpp
CPP_FILES+=../../wx+/image_tools.cpp
CPP_FILES+=../../wx+/graph.cpp
-CPP_FILES+=../../wx+/http.cpp
CPP_FILES+=../../wx+/tooltip.cpp
CPP_FILES+=../../wx+/image_resources.cpp
CPP_FILES+=../../wx+/popup_dlg.cpp
diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile
index e4915afa..161055f4 100755
--- a/FreeFileSync/Source/RealTimeSync/Makefile
+++ b/FreeFileSync/Source/RealTimeSync/Makefile
@@ -2,8 +2,8 @@ APPNAME = RealTimeSync
prefix = /usr
BINDIR = $(DESTDIR)$(prefix)/bin
-CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -I../../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
--Wall -Wfatal-errors -Winit-self -Wmissing-include-dirs -Wswitch-enum -Wmain -Wnon-virtual-dtor -Wcast-align -Wshadow -Wno-deprecated-declarations \
+CXXFLAGS = -std=c++17 -pipe -DWXINTL_NO_GETTEXT_MACRO -I../../.. -I../../../zenXml -isystem../../../boost -include "zen/i18n.h" -include "zen/warn_static.h" \
+-Wall -Wfatal-errors -Wmissing-include-dirs -Wswitch-enum -Wcast-align -Wshadow -Wnon-virtual-dtor \
-O3 -DNDEBUG `wx-config --cxxflags --debug=no` -pthread
LINKFLAGS = -s -no-pie `wx-config --libs std, aui --debug=no` -pthread
@@ -20,9 +20,9 @@ CPP_FILES+=tray_menu.cpp
CPP_FILES+=monitor.cpp
CPP_FILES+=xml_proc.cpp
CPP_FILES+=folder_selector2.cpp
-CPP_FILES+=../lib/localization.cpp
-CPP_FILES+=../lib/resolve_path.cpp
-CPP_FILES+=../lib/ffs_paths.cpp
+CPP_FILES+=../base/localization.cpp
+CPP_FILES+=../base/resolve_path.cpp
+CPP_FILES+=../base/ffs_paths.cpp
CPP_FILES+=../../../zen/xml_io.cpp
CPP_FILES+=../../../zen/dir_watcher.cpp
CPP_FILES+=../../../zen/file_access.cpp
@@ -30,6 +30,7 @@ CPP_FILES+=../../../zen/file_io.cpp
CPP_FILES+=../../../zen/file_traverser.cpp
CPP_FILES+=../../../zen/zstring.cpp
CPP_FILES+=../../../zen/format_unit.cpp
+CPP_FILES+=../../../zen/thread.cpp
CPP_FILES+=../../../wx+/file_drop.cpp
CPP_FILES+=../../../wx+/image_tools.cpp
CPP_FILES+=../../../wx+/image_resources.cpp
diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp
index 5559fcf9..69f7475f 100755
--- a/FreeFileSync/Source/RealTimeSync/application.cpp
+++ b/FreeFileSync/Source/RealTimeSync/application.cpp
@@ -14,12 +14,12 @@
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
#include "xml_proc.h"
-#include "../lib/localization.h"
-#include "../lib/ffs_paths.h"
-#include "../lib/return_codes.h"
-#include "../lib/error_log.h"
-#include "../lib/help_provider.h"
-#include "../lib/resolve_path.h"
+#include "../base/localization.h"
+#include "../base/ffs_paths.h"
+#include "../base/return_codes.h"
+#include "../base/error_log.h"
+#include "../base/help_provider.h"
+#include "../base/resolve_path.h"
#include <gtk/gtk.h>
@@ -142,5 +142,6 @@ void Application::onQueryEndSession(wxEvent& event)
if (auto mainWin = dynamic_cast<MainDialog*>(GetTopWindow()))
mainWin->onQueryEndSession();
//it's futile to try and clean up while the process is in full swing (CRASH!) => just terminate!
- std::abort(); //on Windows calls ::ExitProcess() which can still internally process Window messages and crash!
+ std::exit(fff::FFS_RC_ABORTED);
+ //don't use std::abort() => crashes process with "EXC_CRASH (SIGABRT)" on macOS
}
diff --git a/FreeFileSync/Source/RealTimeSync/application.h b/FreeFileSync/Source/RealTimeSync/application.h
index 338a15e1..bdfc2027 100755
--- a/FreeFileSync/Source/RealTimeSync/application.h
+++ b/FreeFileSync/Source/RealTimeSync/application.h
@@ -16,8 +16,8 @@ class Application : public wxApp
{
public:
bool OnInit() override;
- int OnExit() override;
int OnRun () override;
+ int OnExit() override;
bool OnExceptionInMainLoop() override { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun()
void OnUnhandledException () override { throw; } //just re-throw and avoid display of additional messagebox
void onQueryEndSession(wxEvent& event);
diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
index 64d582dc..97314b4d 100755
--- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
+++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
@@ -11,7 +11,7 @@
#include <wx/dirdlg.h>
#include <wx/scrolwin.h>
#include <wx+/popup_dlg.h>
-#include "../lib/resolve_path.h"
+#include "../base/resolve_path.h"
#include <gtk/gtk.h>
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index 67fdde0a..3fb69547 100755
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -17,9 +17,9 @@
#include "xml_proc.h"
#include "tray_menu.h"
#include "app_icon.h"
-#include "../lib/help_provider.h"
-#include "../lib/process_xml.h"
-#include "../lib/ffs_paths.h"
+#include "../base/help_provider.h"
+//#include "../base/process_xml.h"
+#include "../base/ffs_paths.h"
#include "../version/version.h"
#include <gtk/gtk.h>
diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp
index 8f0f5c18..3b5a4321 100755
--- a/FreeFileSync/Source/RealTimeSync/monitor.cpp
+++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp
@@ -12,7 +12,7 @@
#include <zen/thread.h>
#include <zen/basic_math.h>
#include <wx/utils.h>
-#include "../lib/resolve_path.h"
+#include "../base/resolve_path.h"
//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header
//#include "../library/lock_holder.h" //LOCK_FILE_ENDING
//TEMP_FILE_ENDING
@@ -30,7 +30,7 @@ std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& folderPathPhra
std::set<Zstring, LessFilePath> folderPaths; //make unique
for (const Zstring& phrase : std::set<Zstring, LessFilePath>(folderPathPhrases.begin(), folderPathPhrases.end()))
{
- //hopefully clear enough now: https://www.freefilesync.org/forum/viewtopic.php?t=4302
+ //hopefully clear enough now: https://freefilesync.org/forum/viewtopic.php?t=4302
auto checkProtocol = [&](const Zstring& protoName)
{
if (startsWith(trimCpy(phrase), protoName + Zstr(":"), CmpAsciiNoCase()))
diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
index dd697d1f..fb152755 100755
--- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
+++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
@@ -17,7 +17,7 @@
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
#include "monitor.h"
-#include "../lib/resolve_path.h"
+#include "../base/resolve_path.h"
using namespace zen;
using namespace rts;
diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
index f4762540..dd933e0d 100755
--- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
+++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
@@ -7,8 +7,8 @@
#include "xml_proc.h"
#include <zen/file_access.h>
#include <wx/intl.h>
-#include "../lib/ffs_paths.h"
-#include "../lib/localization.h"
+#include "../base/ffs_paths.h"
+#include "../base/localization.h"
using namespace zen;
using namespace rts;
diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp
index a7d21ccf..8b017923 100755
--- a/FreeFileSync/Source/algorithm.cpp
+++ b/FreeFileSync/Source/base/algorithm.cpp
@@ -12,12 +12,12 @@
#include <zen/guid.h>
#include <zen/file_access.h> //needed for TempFileBuffer only
#include <zen/serialize.h>
-#include "lib/norm_filter.h"
-#include "lib/db_file.h"
-#include "lib/cmp_filetime.h"
-#include "lib/status_handler_impl.h"
-#include "fs/concrete.h"
-#include "fs/native.h"
+#include "norm_filter.h"
+#include "db_file.h"
+#include "cmp_filetime.h"
+#include "status_handler_impl.h"
+#include "../fs/concrete.h"
+#include "../fs/native.h"
using namespace zen;
@@ -1199,14 +1199,14 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
{
auto notifyItemCopy = [&](const std::wstring& statusText, const std::wstring& displayPath)
{
- callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath)));
+ callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw X
};
const std::wstring txtCreatingFile (_("Creating file %x" ));
const std::wstring txtCreatingFolder(_("Creating folder %x" ));
const std::wstring txtCreatingLink (_("Creating symbolic link %x"));
- auto copyItem = [overwriteIfExists](const AbstractPath& targetPath, ItemStatReporter& statReporter, //throw FileError
- const std::function<void(const std::function<void()>& deleteTargetItem)>& copyItemPlain) //throw FileError
+ auto copyItem = [&callback, overwriteIfExists](const AbstractPath& targetPath, ItemStatReporter<>& statReporter, //throw FileError
+ const std::function<void(const std::function<void()>& deleteTargetItem)>& copyItemPlain) //throw FileError
{
//start deleting existing target as required by copyFileTransactional():
//best amortized performance if "target existing" is the most common case
@@ -1239,6 +1239,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
{
AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
statReporter.reportDelta(1, 0);
+ callback.requestUiRefresh(); //throw X
}
//potential future issue when adding multithreading support: intermediate folders might already exist
//potential future issue 2: folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
@@ -1261,7 +1262,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
visitFSObject(*fsObj, [&](const FolderPair& folder)
{
- ItemStatReporter statReporter(1, 0, callback);
+ ItemStatReporter<> statReporter(1, 0, callback);
notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath));
try
{
@@ -1285,6 +1286,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
{
AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
statReporter.reportDelta(1, 0);
+ callback.requestUiRefresh(); //throw X
}
//potential future issue when adding multithreading support: intermediate folders might already exist
//potential future issue 2: parent folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
@@ -1294,7 +1296,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
[&](const FilePair& file)
{
- ItemStatReporter statReporter(1, file.getFileSize<side>(), callback);
+ ItemStatReporter<> statReporter(1, file.getFileSize<side>(), callback);
notifyItemCopy(txtCreatingFile, AFS::getDisplayPath(targetPath));
const FileAttributes attr = file.getAttributes<side>();
@@ -1302,7 +1304,12 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
copyItem(targetPath, statReporter, [&](const std::function<void()>& deleteTargetItem) //throw FileError
{
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta)
+
+ {
+ statReporter.reportDelta(0, bytesDelta);
+ callback.requestUiRefresh(); //throw X
+ };
/*const AFS::FileCopyResult result =*/ AFS::copyFileTransactional(sourcePath, sourceAttr, targetPath, //throw FileError, ErrorFileLocked
false /*copyFilePermissions*/, true /*transactionalCopy*/, deleteTargetItem, notifyUnbufferedIO);
//result.errorModTime? => probably irrelevant (behave like Windows Explorer)
@@ -1312,7 +1319,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
[&](const SymlinkPair& symlink)
{
- ItemStatReporter statReporter(1, 0, callback);
+ ItemStatReporter<> statReporter(1, 0, callback);
notifyItemCopy(txtCreatingLink, AFS::getDisplayPath(targetPath));
copyItem(targetPath, statReporter, [&](const std::function<void()>& deleteTargetItem) //throw FileError
@@ -1322,6 +1329,8 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
});
statReporter.reportDelta(1, 0);
});
+
+ callback.requestUiRefresh(); //throw X
}, callback); //throw X
}
}
@@ -1372,7 +1381,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
{
auto notifyItemDeletion = [&](const std::wstring& statusText, const std::wstring& displayPath)
{
- callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath)));
+ callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw X
};
std::wstring txtRemovingFile;
@@ -1396,7 +1405,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
for (FileSystemObject* fsObj : rowsToDelete) //all pointers are required(!) to be bound
tryReportingError([&]
{
- ItemStatReporter statReporter(1, 0, callback);
+ ItemStatReporter<> statReporter(1, 0, callback);
if (!fsObj->isEmpty<side>()) //element may be implicitly deleted, e.g. if parent folder was deleted first
{
@@ -1405,7 +1414,8 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
{
if (useRecycleBin)
{
- notifyItemDeletion(txtRemovingDirectory, AFS::getDisplayPath(folder.getAbstractPath<side>()));
+ notifyItemDeletion(txtRemovingDirectory, AFS::getDisplayPath(folder.getAbstractPath<side>())); //throw X
+
AFS::recycleItemIfExists(folder.getAbstractPath<side>()); //throw FileError
statReporter.reportDelta(1, 0);
}
@@ -1413,13 +1423,13 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
{
auto onBeforeFileDeletion = [&](const std::wstring& displayPath)
{
+ notifyItemDeletion(txtRemovingFile, displayPath); //throw X
statReporter.reportDelta(1, 0);
- notifyItemDeletion(txtRemovingFile, displayPath);
};
auto onBeforeDirDeletion = [&](const std::wstring& displayPath)
{
+ notifyItemDeletion(txtRemovingDirectory, displayPath); //throw X
statReporter.reportDelta(1, 0);
- notifyItemDeletion(txtRemovingDirectory, displayPath);
};
AFS::removeFolderIfExistsRecursion(folder.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError
@@ -1428,7 +1438,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
[&](const FilePair& file)
{
- notifyItemDeletion(txtRemovingFile, AFS::getDisplayPath(file.getAbstractPath<side>()));
+ notifyItemDeletion(txtRemovingFile, AFS::getDisplayPath(file.getAbstractPath<side>())); //throw X
if (useRecycleBin)
AFS::recycleItemIfExists(file.getAbstractPath<side>()); //throw FileError
@@ -1439,7 +1449,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
[&](const SymlinkPair& symlink)
{
- notifyItemDeletion(txtRemovingSymlink, AFS::getDisplayPath(symlink.getAbstractPath<side>()));
+ notifyItemDeletion(txtRemovingSymlink, AFS::getDisplayPath(symlink.getAbstractPath<side>())); //throw X
if (useRecycleBin)
AFS::recycleItemIfExists(symlink.getAbstractPath<side>()); //throw FileError
@@ -1450,6 +1460,9 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete,
fsObj->removeObject<side>(); //if directory: removes recursively!
}
+
+ //remain transactional as much as possible => allow for abort only *after* updating file model
+ callback.requestUiRefresh(); //throw X
}, callback); //throw X
}
@@ -1460,7 +1473,7 @@ void categorize(const std::vector<FileSystemObject*>& rows,
std::vector<FileSystemObject*>& deleteRecyler,
bool useRecycleBin,
std::map<AbstractPath, bool>& recyclerSupported,
- ProcessCallback& callback)
+ ProcessCallback& callback) //throw X
{
auto hasRecycler = [&](const AbstractPath& baseFolderPath) -> bool
{
@@ -1472,7 +1485,7 @@ void categorize(const std::vector<FileSystemObject*>& rows,
bool recSupported = false;
tryReportingError([&]{
- recSupported = AFS::supportsRecycleBin(baseFolderPath, [&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError
+ recSupported = AFS::supportsRecycleBin(baseFolderPath, [&] { callback.reportStatus(msg); /*throw X*/ }); //throw FileError
}, callback); //throw X
recyclerSupported.emplace(baseFolderPath, recSupported);
@@ -1566,8 +1579,8 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
std::vector<FileSystemObject*> deleteRecylerRight;
std::map<AbstractPath, bool> recyclerSupported;
- categorize< LEFT_SIDE>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback);
- categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback);
+ categorize< LEFT_SIDE>(deleteLeft, deletePermanentLeft, deleteRecylerLeft, useRecycleBin, recyclerSupported, callback); //throw X
+ categorize<RIGHT_SIDE>(deleteRight, deletePermanentRight, deleteRecylerRight, useRecycleBin, recyclerSupported, callback); //
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
if (useRecycleBin &&
@@ -1644,7 +1657,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
if (tempFolderPath_.empty())
{
- Opt<std::wstring> errMsg = tryReportingError([&]
+ const std::wstring errMsg = tryReportingError([&]
{
//generate random temp folder path e.g. C:\Users\Zenju\AppData\Local\Temp\FFS-068b2e88
Zstring tempPathTmp = appendSeparator(getTempFolderPath()); //throw FileError
@@ -1657,7 +1670,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
tempFolderPath_ = tempPathTmp;
}, callback); //throw X
- if (errMsg) return;
+ if (!errMsg.empty()) return;
}
for (const FileDescriptor& descr : workLoad)
@@ -1684,20 +1697,24 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
tryReportingError([&]
{
- ItemStatReporter statReporter(1, descr.attr.fileSize, callback);
+ ItemStatReporter<> statReporter(1, descr.attr.fileSize, callback);
- callback.reportInfo(replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath)));
-
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ callback.reportInfo(replaceCpy(_("Creating file %x"), L"%x", fmtPath(tempFilePath))); //throw X
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta)
+ {
+ statReporter.reportDelta(0, bytesDelta);
+ callback.requestUiRefresh(); //throw X
+ };
/*const AFS::FileCopyResult result =*/ AFS::copyFileTransactional(descr.path, sourceAttr, //throw FileError, ErrorFileLocked
createItemPathNative(tempFilePath),
false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, notifyUnbufferedIO);
//result.errorModTime? => irrelevant for temp files!
-
statReporter.reportDelta(1, 0);
tempFilePaths_[descr] = tempFilePath;
}, callback); //throw X
+
+ callback.requestUiRefresh(); //throw X
}
}
diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/base/algorithm.h
index f3a81e12..178e056e 100755
--- a/FreeFileSync/Source/algorithm.h
+++ b/FreeFileSync/Source/base/algorithm.h
@@ -9,8 +9,8 @@
#include <functional>
#include "file_hierarchy.h"
-#include "lib/soft_filter.h"
-#include "lib/process_xml.h"
+#include "soft_filter.h"
+#include "process_xml.h"
#include "process_callback.h"
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/base/application.cpp
index f4519639..6169031d 100755
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/base/application.cpp
@@ -16,12 +16,12 @@
#include "comparison.h"
#include "algorithm.h"
#include "synchronization.h"
-#include "ui/batch_status_handler.h"
-#include "ui/main_dlg.h"
-#include "lib/help_provider.h"
-#include "lib/process_xml.h"
-#include "lib/error_log.h"
-#include "lib/resolve_path.h"
+#include "help_provider.h"
+#include "process_xml.h"
+#include "error_log.h"
+#include "resolve_path.h"
+#include "../ui/batch_status_handler.h"
+#include "../ui/main_dlg.h"
#include <gtk/gtk.h>
@@ -49,7 +49,6 @@ const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType();
//##################################################################################################################
-
bool Application::OnInit()
{
//do not call wxApp::OnInit() to avoid using wxWidgets command line parser
@@ -130,7 +129,8 @@ void Application::onQueryEndSession(wxEvent& event)
if (auto mainWin = dynamic_cast<MainDialog*>(GetTopWindow()))
mainWin->onQueryEndSession();
//it's futile to try and clean up while the process is in full swing (CRASH!) => just terminate!
- std::abort(); //on Windows calls ::ExitProcess() which can still internally process Window messages and crash!
+ std::exit(FFS_RC_ABORTED);
+ //don't use std::abort() => crashes process with "EXC_CRASH (SIGABRT)" on macOS
}
diff --git a/FreeFileSync/Source/application.h b/FreeFileSync/Source/base/application.h
index 1b930074..c08491c8 100755
--- a/FreeFileSync/Source/application.h
+++ b/FreeFileSync/Source/base/application.h
@@ -10,7 +10,7 @@
#include <vector>
#include <zen/zstring.h>
#include <wx/app.h>
-#include "lib/return_codes.h"
+#include "return_codes.h"
namespace fff //avoid name clash with "int ffs()" for fuck's sake! (maxOS, Linux issue only: <string> internally includes <strings.h>, WTF!)
@@ -19,7 +19,7 @@ class Application : public wxApp
{
private:
bool OnInit() override;
- int OnRun() override;
+ int OnRun () override;
int OnExit() override;
bool OnExceptionInMainLoop() override { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun()
void OnUnhandledException () override { throw; } //just re-throw and avoid display of additional messagebox
diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/base/binary.cpp
index bfa5cb97..32fe37a6 100755
--- a/FreeFileSync/Source/lib/binary.cpp
+++ b/FreeFileSync/Source/base/binary.cpp
@@ -44,7 +44,7 @@ struct StreamReader
defaultBlockSize_(stream_->getBlockSize()),
dynamicBlockSize_(defaultBlockSize_) { assert(defaultBlockSize_ > 0); }
- void appendChunk(std::vector<char>& buffer) //throw FileError, X
+ void appendChunk(std::vector<std::byte>& buffer) //throw FileError, X
{
assert(!eof_);
if (eof_) return;
@@ -104,8 +104,8 @@ bool fff::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath
StreamReader* readerLow = &reader1;
StreamReader* readerHigh = &reader2;
- std::vector<char> bufferLow;
- std::vector<char> bufferHigh;
+ std::vector<std::byte> bufferLow;
+ std::vector<std::byte> bufferHigh;
for (;;)
{
diff --git a/FreeFileSync/Source/lib/binary.h b/FreeFileSync/Source/base/binary.h
index bba321da..bba321da 100755
--- a/FreeFileSync/Source/lib/binary.h
+++ b/FreeFileSync/Source/base/binary.h
diff --git a/FreeFileSync/Source/lib/cmp_filetime.h b/FreeFileSync/Source/base/cmp_filetime.h
index ce3a04e9..ce3a04e9 100755
--- a/FreeFileSync/Source/lib/cmp_filetime.h
+++ b/FreeFileSync/Source/base/cmp_filetime.h
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/base/comparison.cpp
index b0e7bcec..75aaee7f 100755
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/base/comparison.cpp
@@ -8,12 +8,12 @@
#include <zen/process_priority.h>
#include <zen/perf.h>
#include "algorithm.h"
-#include "lib/parallel_scan.h"
-#include "lib/dir_exist_async.h"
-#include "lib/binary.h"
-#include "lib/cmp_filetime.h"
-#include "lib/status_handler_impl.h"
-#include "fs/concrete.h"
+#include "parallel_scan.h"
+#include "dir_exist_async.h"
+#include "binary.h"
+#include "cmp_filetime.h"
+#include "status_handler_impl.h"
+#include "../fs/concrete.h"
using namespace zen;
using namespace fff;
@@ -73,7 +73,7 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCf
{
std::set<AbstractPath> uniqueBaseFolders;
- //support "retry" for environment variable and and variable driver letter resolution!
+ //support "retry" for environment variable and variable driver letter resolution!
output.resolvedPairs.clear();
for (const FolderPairCfg& fpCfg : fpCfgList)
{
@@ -143,7 +143,8 @@ private:
std::map<DirectoryKey, DirectoryValue> directoryBuffer_; //contains only *existing* directories
const int fileTimeTolerance_;
- ProcessCallback& callback_;
+ ProcessCallback& cb_;
+ const std::map<AbstractPath, size_t> deviceParallelOps_;
};
@@ -151,24 +152,24 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
const std::map<AbstractPath, size_t>& deviceParallelOps,
int fileTimeTolerance,
ProcessCallback& callback) :
- fileTimeTolerance_(fileTimeTolerance), callback_(callback)
+ fileTimeTolerance_(fileTimeTolerance), cb_(callback), deviceParallelOps_(deviceParallelOps)
{
class CbImpl : public FillBufferCallback
{
public:
- CbImpl(ProcessCallback& pcb) : callback_(pcb) {}
+ CbImpl(ProcessCallback& pcb) : cb_(pcb) {}
- void reportStatus(const std::wstring& statusMsg, int itemsTotal) override
+ void reportStatus(const std::wstring& statusMsg, int itemsTotal) override //throw X
{
- callback_.updateDataProcessed(itemsTotal - itemsReported_, 0); //processed bytes are reported in subfunctions!
+ cb_.updateDataProcessed(itemsTotal - itemsReported_, 0); //processed bytes are reported in subfunctions!
itemsReported_ = itemsTotal;
- callback_.reportStatus(statusMsg); //may throw
+ cb_.reportStatus(statusMsg); //throw X
}
HandleError reportError(const std::wstring& msg, size_t retryNumber) override
{
- switch (callback_.reportError(msg, retryNumber))
+ switch (cb_.reportError(msg, retryNumber))
{
case ProcessCallback::IGNORE_ERROR:
return ON_ERROR_CONTINUE;
@@ -184,17 +185,17 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
int getItemsTotal() const { return itemsReported_; }
private:
- ProcessCallback& callback_;
+ ProcessCallback& cb_;
int itemsReported_ = 0;
} cb(callback);
- fillBuffer(foldersToRead, //in
+ fillBuffer(foldersToRead, //in
directoryBuffer_, //out
deviceParallelOps,
- cb,
+ cb, //throw X
UI_UPDATE_INTERVAL / 2); //every ~50 ms
- callback.reportInfo(_("Comparison finished:") + L" " + _P("1 item found", "%x items found", cb.getItemsTotal()));
+ callback.reportInfo(_("Comparison finished:") + L" " + _P("1 item found", "%x items found", cb.getItemsTotal())); //throw X
}
@@ -209,41 +210,41 @@ const wchar_t arrowRight[] = L"->";
// => only add path info if information is relevant, e.g. conflict is specific to left/right side only
template <SelectedSide side, class FileOrLinkPair> inline
-std::wstring getConflictInvalidDate(const FileOrLinkPair& file)
+Zstringw getConflictInvalidDate(const FileOrLinkPair& file)
{
- return replaceCpy(_("File %x has an invalid date."), L"%x", fmtPath(AFS::getDisplayPath(file.template getAbstractPath<side>()))) + L"\n" +
- _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<side>());
+ return copyStringTo<Zstringw>(replaceCpy(_("File %x has an invalid date."), L"%x", fmtPath(AFS::getDisplayPath(file.template getAbstractPath<side>()))) + L"\n" +
+ _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<side>()));
}
-std::wstring getConflictSameDateDiffSize(const FilePair& file)
+Zstringw getConflictSameDateDiffSize(const FilePair& file)
{
- return _("Files have the same date but a different size.") + L"\n" +
- arrowLeft + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L" " + formatNumber(file.getFileSize<LEFT_SIDE>()) + L"\n" +
- arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + formatNumber(file.getFileSize<RIGHT_SIDE>());
+ return copyStringTo<Zstringw>(_("Files have the same date but a different size.") + L"\n" +
+ arrowLeft + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L" " + formatNumber(file.getFileSize<LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + formatNumber(file.getFileSize<RIGHT_SIDE>()));
}
-std::wstring getConflictSkippedBinaryComparison(const FilePair& file)
+Zstringw getConflictSkippedBinaryComparison(const FilePair& file)
{
- return _("Content comparison was skipped for excluded files.");
+ return copyStringTo<Zstringw>(_("Content comparison was skipped for excluded files."));
}
-std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
+Zstringw getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
{
- return _("Items differ in attributes only") + L"\n" +
- arrowLeft + L" " + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L"\n" +
- arrowRight + L" " + fmtPath(fsObj.getItemName<RIGHT_SIDE>());
+ return copyStringTo<Zstringw>(_("Items differ in attributes only") + L"\n" +
+ arrowLeft + L" " + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + fmtPath(fsObj.getItemName<RIGHT_SIDE>()));
}
template <class FileOrLinkPair>
-std::wstring getDescrDiffMetaDate(const FileOrLinkPair& file)
+Zstringw getDescrDiffMetaDate(const FileOrLinkPair& file)
{
- return _("Items differ in attributes only") + L"\n" +
- arrowLeft + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime< LEFT_SIDE>()) + L"\n" +
- arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<RIGHT_SIDE>());
+ return copyStringTo<Zstringw>(_("Items differ in attributes only") + L"\n" +
+ arrowLeft + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<RIGHT_SIDE>()));
}
//-----------------------------------------------------------------------------
@@ -338,22 +339,24 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
}
+namespace
+{
void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback)
{
//categorize symlinks that exist on both sides
std::string binaryContentL;
std::string binaryContentR;
- Opt<std::wstring> errMsg = tryReportingError([&]
+ const std::wstring errMsg = tryReportingError([&]
{
- callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>()))));
+ callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>())))); //throw X
binaryContentL = AFS::getSymlinkBinaryContent(symlink.getAbstractPath<LEFT_SIDE>()); //throw FileError
- callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>()))));
+ callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>())))); //throw X
binaryContentR = AFS::getSymlinkBinaryContent(symlink.getAbstractPath<RIGHT_SIDE>()); //throw FileError
}, callback); //throw X
- if (errMsg)
- symlink.setCategoryConflict(*errMsg);
+ if (!errMsg.empty())
+ symlink.setCategoryConflict(copyStringTo<Zstringw>(errMsg));
else
{
if (binaryContentL == binaryContentR)
@@ -375,6 +378,7 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback)
symlink.setCategory<FILE_DIFFERENT_CONTENT>();
}
}
+}
std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareBySize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const
@@ -386,7 +390,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareBySize(const ResolvedFo
//finish symlink categorization
for (SymlinkPair* symlink : uncategorizedLinks)
- categorizeSymlinkByContent(*symlink, callback_); //"compare by size" has the semantics of a quick content-comparison!
+ categorizeSymlinkByContent(*symlink, cb_); //"compare by size" has the semantics of a quick content-comparison!
//harmonize with algorithm.cpp, stillInSync()!
//categorize files that exist on both sides
@@ -410,17 +414,113 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareBySize(const ResolvedFo
}
+namespace parallel
+{
+//--------------------------------------------------------------
+//ATTENTION CALLBACKS: they also run asynchronously *outside* the singleThread lock!
+//--------------------------------------------------------------
+inline
+bool filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, //throw FileError
+ const zen::IOCallback& notifyUnbufferedIO, //may be nullptr
+ std::mutex& singleThread)
+{ return parallelScope([=] { return filesHaveSameContent(filePath1, filePath2, notifyUnbufferedIO); /*throw FileError*/ }, singleThread); }
+}
+
+namespace
+{
+void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingContentOfFiles, AsyncCallback& acb, std::mutex& singleThread) //throw ThreadInterruption
+{
+ acb.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtPath(file.getPairRelativePath()))); //throw ThreadInterruption
+
+ bool haveSameContent = false;
+ const std::wstring errMsg = tryReportingError([&]
+ {
+ AsyncItemStatReporter statReporter(1, file.getFileSize<LEFT_SIDE>(), acb);
+
+ //callbacks run *outside* singleThread_ lock! => fine
+ auto notifyUnbufferedIO = [&statReporter](int64_t bytesDelta)
+ {
+ statReporter.reportDelta(0, bytesDelta);
+ interruptionPoint(); //throw ThreadInterruption
+ };
+
+ haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath<LEFT_SIDE >(),
+ file.getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO, singleThread); //throw FileError
+ statReporter.reportDelta(1, 0);
+ }, acb); //throw ThreadInterruption
+
+ if (!errMsg.empty())
+ file.setCategoryConflict(copyStringTo<Zstringw>(errMsg));
+ else
+ {
+ if (haveSameContent)
+ {
+ //Caveat:
+ //1. FILE_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
+ //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile
+ //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
+ if (file.getItemName<LEFT_SIDE>() != file.getItemName<RIGHT_SIDE>())
+ file.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(file));
+#if 0 //don't synchronize modtime only see FolderPairSyncer::synchronizeFileInt(), SO_COPY_METADATA_TO_*
+ else if (!sameFileTime(file.getLastWriteTime<LEFT_SIDE>(),
+ file.getLastWriteTime<RIGHT_SIDE>(), file.base().getFileTimeTolerance(), file.base().getIgnoredTimeShift()))
+ file.setCategoryDiffMetadata(getDescrDiffMetaDate(file));
+#endif
+ else
+ file.setCategory<FILE_EQUAL>();
+ }
+ else
+ file.setCategory<FILE_DIFFERENT_CONTENT>();
+ }
+}
+}
+
+
std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(const std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>>& workLoad) const
{
- warn_static("perf: make parallel")
- std::list<std::shared_ptr<BaseFolderPair>> output;
- if (workLoad.empty())
- return output;
+ struct ParallelOps
+ {
+ size_t current = 0;
+ size_t effectiveMax = 0; //a folder pair is allowed to use the maximum parallelOps that left/right devices support
+ // => consider max over all folder pairs, that a device is involved with!
+ };
+ std::map<AbstractPath, ParallelOps> parallelOpsStatus;
+
+ struct BinaryWorkload
+ {
+ ParallelOps& parallelOpsL; //
+ ParallelOps& parallelOpsR; //consider aliasing!
+ RingBuffer<FilePair*> filesToCompareBytewise;
+ };
+ std::vector<BinaryWorkload> fpWorkload;
+
+ auto getDefaultParallelOps = [&](const AbstractPath& rootPath)
+ {
+ auto itParOps = deviceParallelOps_.find(rootPath);
+ return std::max<size_t>(itParOps != deviceParallelOps_.end() ? itParOps->second : 1, 1); //sanitize early for correct status display
+ };
+
+ auto addToBinaryWorkload = [&](const AbstractPath& basePathL, const AbstractPath& basePathR, RingBuffer<FilePair*>&& filesToCompareBytewise)
+ {
+ const AbstractPath rootPathL = AFS::getPathComponents(basePathL).rootPath;
+ const AbstractPath rootPathR = AFS::getPathComponents(basePathR).rootPath;
+
+ //calculate effective max parallelOps that devices must support
+ const size_t parallelOpsFp = std::max(getDefaultParallelOps(rootPathL),
+ getDefaultParallelOps(rootPathR));
+
+ ParallelOps& posL = parallelOpsStatus[rootPathL];
+ ParallelOps& posR = parallelOpsStatus[rootPathR];
+
+ posL.effectiveMax = std::max(posL.effectiveMax, parallelOpsFp);
+ posR.effectiveMax = std::max(posR.effectiveMax, parallelOpsFp);
+
+ fpWorkload.push_back({ posL, posR, std::move(filesToCompareBytewise) });
+ };
//PERF_START;
- std::vector<FilePair*> filesToCompareBytewise;
+ std::list<std::shared_ptr<BaseFolderPair>> output;
- //process folder pairs one after another
for (const auto& w : workLoad)
{
std::vector<FilePair*> undefinedFiles;
@@ -431,6 +531,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
//content comparison of file content happens AFTER finding corresponding files and AFTER filtering
//in order to separate into two processes (scanning and comparing)
+ RingBuffer<FilePair*> filesToCompareBytewise;
for (FilePair* file : undefinedFiles)
//pre-check: files have different content if they have a different filesize (must not be FILE_EQUAL: see InSyncFile)
if (file->getFileSize<LEFT_SIDE>() != file->getFileSize<RIGHT_SIDE>())
@@ -444,68 +545,95 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
else
filesToCompareBytewise.push_back(file);
}
+ if (!filesToCompareBytewise.empty())
+ addToBinaryWorkload(output.back()->getAbstractPath<LEFT_SIDE >(),
+ output.back()->getAbstractPath<RIGHT_SIDE>(), std::move(filesToCompareBytewise));
//finish symlink categorization
for (SymlinkPair* symlink : uncategorizedLinks)
- categorizeSymlinkByContent(*symlink, callback_);
+ categorizeSymlinkByContent(*symlink, cb_);
}
- //finish categorization...
- const int itemsTotal = static_cast<int>(filesToCompareBytewise.size());
+ //finish categorization: compare files (that have same size) bytewise...
+ if (!fpWorkload.empty()) //run PHASE_COMPARING_CONTENT only when needed
+ {
+ int itemsTotal = 0;
+ uint64_t bytesTotal = 0;
+ for (const BinaryWorkload& bwl : fpWorkload)
+ {
+ itemsTotal += bwl.filesToCompareBytewise.size();
- uint64_t bytesTotal = 0; //left and right filesizes are equal
- for (FilePair* file : filesToCompareBytewise)
- bytesTotal += file->getFileSize<LEFT_SIDE>();
+ for (const FilePair* file : bwl.filesToCompareBytewise)
+ bytesTotal += file->getFileSize<LEFT_SIDE>(); //left and right file sizes are equal
+ }
+ cb_.initNewPhase(itemsTotal, bytesTotal, ProcessCallback::PHASE_COMPARING_CONTENT); //throw X
- callback_.initNewPhase(itemsTotal, bytesTotal, ProcessCallback::PHASE_COMPARING_CONTENT); //may throw
+ //PERF_START;
- const std::wstring txtComparingContentOfFiles = _("Comparing content of files %x");
+ warn_static("review")
- //PERF_START;
+ std::mutex singleThread; //only a single worker thread may run at a time, except for parallel file I/O
- //compare files (that have same size) bytewise...
- for (FilePair* file : filesToCompareBytewise)
- {
- callback_.reportStatus(replaceCpy(txtComparingContentOfFiles, L"%x", fmtPath(file->getPairRelativePath())));
+ AsyncCallback acb; //
+ std::function<void()> scheduleMoreTasks; //manage life time: enclose ThreadGroup!
+ const std::wstring txtComparingContentOfFiles = _("Comparing content of files %x"); //
- //check files that exist in left and right model but have different content
+ ThreadGroup<std::function<void()>> tg(std::numeric_limits<size_t>::max(), "Binary Comparison");
- bool haveSameContent = false;
- Opt<std::wstring> errMsg = tryReportingError([&]
+ scheduleMoreTasks = [&]
{
- ItemStatReporter statReporter(1, file->getFileSize<LEFT_SIDE>(), callback_);
+ bool wereDone = true;
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); };
+ for (size_t j = 0; j < fpWorkload.size(); ++j)
+ {
+ BinaryWorkload& bwl = fpWorkload[j];
- haveSameContent = filesHaveSameContent(file->getAbstractPath<LEFT_SIDE>(),
- file->getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO); //throw FileError
- statReporter.reportDelta(1, 0);
- }, callback_); //throw X
+ ParallelOps& posL = bwl.parallelOpsL;
+ ParallelOps& posR = bwl.parallelOpsR;
- if (errMsg)
- file->setCategoryConflict(*errMsg);
- else
- {
- if (haveSameContent)
- {
- //Caveat:
- //1. FILE_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
- //2. FILE_EQUAL is expected to mean identical file sizes! See InSyncFile
- //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
- if (file->getItemName<LEFT_SIDE>() != file->getItemName<RIGHT_SIDE>())
- file->setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(*file));
-#if 0 //don't synchronize modtime only see FolderPairSyncer::synchronizeFileInt(), SO_COPY_METADATA_TO_*
- else if (!sameFileTime(file->getLastWriteTime<LEFT_SIDE>(),
- file->getLastWriteTime<RIGHT_SIDE>(), file->base().getFileTimeTolerance(), file->base().getIgnoredTimeShift()))
- file->setCategoryDiffMetadata(getDescrDiffMetaDate(*file));
-#endif
- else
- file->setCategory<FILE_EQUAL>();
+ const size_t newTaskCount = numeric::min<size_t>(posL.effectiveMax - posL.current,
+ posR.effectiveMax - posR.current,
+ bwl.filesToCompareBytewise.size());
+ if (&posL != &posR) posL.current += newTaskCount; //
+ /**/ posR.current += newTaskCount; //consider aliasing!
+
+ for (size_t i = 0; i < newTaskCount; ++i)
+ {
+ tg.run([&, statusPrio = j, &file = *bwl.filesToCompareBytewise.front()]
+ {
+ acb.notifyTaskBegin(statusPrio); //prioritize status messages according to natural order of folder pairs
+ ZEN_ON_SCOPE_EXIT(acb.notifyTaskEnd());
+
+ std::lock_guard<std::mutex> dummy(singleThread); //protect ALL variable accesses unless explicitly not needed ("parallel" scope)!
+ //---------------------------------------------------------------------------------------------------
+ ZEN_ON_SCOPE_SUCCESS(if (&posL != &posR) --posL.current;
+ /**/ --posR.current;
+ scheduleMoreTasks(););
+
+ categorizeFileByContent(file, txtComparingContentOfFiles, acb, singleThread); //throw ThreadInterruption
+ });
+
+ bwl.filesToCompareBytewise.pop_front();
+ }
+
+ assert(0 <= posL.current && posL.current <= posL.effectiveMax);
+ assert(0 <= posR.current && posR.current <= posR.effectiveMax);
+
+ if (posL.current != 0 || posR.current != 0 || !bwl.filesToCompareBytewise.empty())
+ wereDone = false;
}
- else
- file->setCategory<FILE_DIFFERENT_CONTENT>();
+ if (wereDone)
+ acb.notifyAllDone();
+ };
+
+ {
+ std::lock_guard<std::mutex> dummy(singleThread); //[!] potential race with worker threads!
+ scheduleMoreTasks(); //set initial load
}
+
+ acb.waitUntilDone(UI_UPDATE_INTERVAL / 2 /*every ~50 ms*/, cb_); //throw X
}
+
return output;
}
@@ -514,7 +642,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
class MergeSides
{
public:
- MergeSides(const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads,
+ MergeSides(const std::map<Zstring, Zstringw, LessFilePath>& failedItemReads,
std::vector<FilePair*>& undefinedFilesOut,
std::vector<SymlinkPair*>& undefinedSymlinksOut) :
failedItemReads_(failedItemReads),
@@ -531,21 +659,21 @@ public:
}
private:
- void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, ContainerObject& output);
+ void mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const Zstringw* errorMsg, ContainerObject& output);
template <SelectedSide side>
- void fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, ContainerObject& output);
+ void fillOneSide(const FolderContainer& folderCont, const Zstringw* errorMsg, ContainerObject& output);
- const std::wstring* checkFailedRead(FileSystemObject& fsObj, const std::wstring* errorMsg);
+ const Zstringw* checkFailedRead(FileSystemObject& fsObj, const Zstringw* errorMsg);
- const std::map<Zstring, std::wstring, LessFilePath>& failedItemReads_; //base-relative paths or empty if read-error for whole base directory
+ const std::map<Zstring, Zstringw, LessFilePath>& failedItemReads_; //base-relative paths or empty if read-error for whole base directory
std::vector<FilePair*>& undefinedFiles_;
std::vector<SymlinkPair*>& undefinedSymlinks_;
};
inline
-const std::wstring* MergeSides::checkFailedRead(FileSystemObject& fsObj, const std::wstring* errorMsg)
+const Zstringw* MergeSides::checkFailedRead(FileSystemObject& fsObj, const Zstringw* errorMsg)
{
if (!errorMsg)
{
@@ -557,14 +685,15 @@ const std::wstring* MergeSides::checkFailedRead(FileSystemObject& fsObj, const s
if (errorMsg)
{
fsObj.setActive(false);
- fsObj.setCategoryConflict(*errorMsg);
+ fsObj.setCategoryConflict(*errorMsg); //peak memory: Zstringw is ref-counted, unlike std::wstring!
+ static_assert(std::is_same_v<const Zstringw&, decltype(*errorMsg)>);
}
return errorMsg;
}
template <SelectedSide side>
-void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstring* errorMsg, ContainerObject& output)
+void MergeSides::fillOneSide(const FolderContainer& folderCont, const Zstringw* errorMsg, ContainerObject& output)
{
for (const auto& file : folderCont.files)
{
@@ -581,7 +710,7 @@ void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstri
for (const auto& dir : folderCont.folders)
{
FolderPair& newFolder = output.addSubFolder<side>(dir.first, dir.second.first);
- const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
fillOneSide<side>(dir.second.second, errorMsgNew, newFolder); //recurse
}
}
@@ -628,9 +757,9 @@ void linearMerge(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOnl
}
-void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const std::wstring* errorMsg, ContainerObject& output)
+void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer& rhs, const Zstringw* errorMsg, ContainerObject& output)
{
- using FileData = const FolderContainer::FileList::value_type;
+ using FileData = FolderContainer::FileList::value_type;
linearMerge(lhs.files, rhs.files,
[&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
@@ -640,16 +769,16 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
{
FilePair& newItem = output.addSubFile(fileLeft.first,
fileLeft.second,
- FILE_EQUAL, //dummy-value until categorization is finished later
+ FILE_CONFLICT, //dummy-value until categorization is finished later
fileRight.first,
fileRight.second);
if (!checkFailedRead(newItem, errorMsg))
undefinedFiles_.push_back(&newItem);
- static_assert(IsSameType<ContainerObject::FileList, FixedList<FilePair>>::value, ""); //ContainerObject::addSubFile() must NOT invalidate references used in "undefinedFiles"!
+ static_assert(std::is_same_v<ContainerObject::FileList, std::list<FilePair>>); //ContainerObject::addSubFile() must NOT invalidate references used in "undefinedFiles"!
});
//-----------------------------------------------------------------------------------------------
- using SymlinkData = const FolderContainer::SymlinkList::value_type;
+ using SymlinkData = FolderContainer::SymlinkList::value_type;
linearMerge(lhs.symlinks, rhs.symlinks,
[&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
@@ -659,7 +788,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
{
SymlinkPair& newItem = output.addSubLink(symlinkLeft.first,
symlinkLeft.second,
- SYMLINK_EQUAL, //dummy-value until categorization is finished later
+ SYMLINK_CONFLICT, //dummy-value until categorization is finished later
symlinkRight.first,
symlinkRight.second);
if (!checkFailedRead(newItem, errorMsg))
@@ -667,26 +796,26 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
});
//-----------------------------------------------------------------------------------------------
- using FolderData = const FolderContainer::FolderList::value_type;
+ using FolderData = FolderContainer::FolderList::value_type;
linearMerge(lhs.folders, rhs.folders,
[&](const FolderData& dirLeft) //left only
{
FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first, dirLeft.second.first);
- const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
this->fillOneSide<LEFT_SIDE>(dirLeft.second.second, errorMsgNew, newFolder); //recurse
},
[&](const FolderData& dirRight) //right only
{
FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first, dirRight.second.first);
- const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
this->fillOneSide<RIGHT_SIDE>(dirRight.second.second, errorMsgNew, newFolder); //recurse
},
[&](const FolderData& dirLeft, const FolderData& dirRight) //both sides
{
FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirLeft.second.first, DIR_EQUAL, dirRight.first, dirRight.second.first);
- const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
if (!errorMsgNew)
if (dirLeft.first != dirRight.first)
@@ -706,8 +835,8 @@ void stripExcludedDirectories(ContainerObject& hierObj, const HardFilter& filter
//remove superfluous directories:
// this does not invalidate "std::vector<FilePair*>& undefinedFiles", since we delete folders only
- // and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to zen::FixedList!
- static_assert(IsSameType<FixedList<FolderPair>, ContainerObject::FolderList>::value, "");
+ // and there is no side-effect for memory positions of FilePair and SymlinkPair thanks to std::list!
+ static_assert(std::is_same_v<std::list<FolderPair>, ContainerObject::FolderList>);
hierObj.refSubFolders().remove_if([&](FolderPair& folder)
{
@@ -730,8 +859,8 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
std::vector<FilePair*>& undefinedFiles,
std::vector<SymlinkPair*>& undefinedSymlinks) const
{
- callback_.reportStatus(_("Generating file list..."));
- callback_.forceUiRefresh(); //throw X
+ cb_.reportStatus(_("Generating file list...")); //throw X
+ cb_.forceUiRefresh(); //throw X
auto getDirValue = [&](const AbstractPath& folderPath) -> const DirectoryValue*
{
@@ -742,16 +871,21 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
const DirectoryValue* bufValueLeft = getDirValue(fp.folderPathLeft);
const DirectoryValue* bufValueRight = getDirValue(fp.folderPathRight);
- std::map<Zstring, std::wstring, LessFilePath> failedReads; //base-relative paths or empty if read-error for whole base directory
+ std::map<Zstring, Zstringw, LessFilePath> failedReads; //base-relative paths or empty if read-error for whole base directory
{
+ auto append = [&](const std::map<Zstring, std::wstring, LessFilePath>& c)
+ {
+ for (const auto& item : c)
+ failedReads.emplace(item.first, copyStringTo<Zstringw>(item.second));
+ };
//mix failedFolderReads with failedItemReads:
//mark directory errors already at directory-level (instead for child items only) to show on GUI! See "MergeSides"
//=> minor pessimization for "excludefilterFailedRead" which needlessly excludes parent folders, too
- if (bufValueLeft ) append(failedReads, bufValueLeft ->failedFolderReads);
- if (bufValueRight) append(failedReads, bufValueRight->failedFolderReads);
+ if (bufValueLeft ) append(bufValueLeft ->failedFolderReads);
+ if (bufValueRight) append(bufValueRight->failedFolderReads);
- if (bufValueLeft ) append(failedReads, bufValueLeft ->failedItemReads);
- if (bufValueRight) append(failedReads, bufValueRight->failedItemReads);
+ if (bufValueLeft ) append(bufValueLeft ->failedItemReads);
+ if (bufValueRight) append(bufValueRight->failedItemReads);
}
Zstring excludefilterFailedRead;
@@ -822,7 +956,7 @@ void fff::logNonDefaultSettings(const XmlGlobalSettings& activeSettings, Process
changedSettingsMsg += L"\n " + _("Verify copied files") + L" - " + (activeSettings.verifyFileCopy ? _("Enabled") : _("Disabled"));
if (!changedSettingsMsg.empty())
- callback.reportInfo(_("Using non-default global settings:") + changedSettingsMsg);
+ callback.reportInfo(_("Using non-default global settings:") + changedSettingsMsg); //throw X
}
@@ -841,7 +975,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
//indicator at the very beginning of the log to make sense of "total time"
//init process: keep at beginning so that all gui elements are initialized properly
- callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //may throw; it's unknown how many files will be scanned => -1 objects
+ callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //throw X; it's unknown how many files will be scanned => -1 objects
//callback.reportInfo(Comparison started")); -> still useful?
//-------------------------------------------------------------------------------
@@ -855,7 +989,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
}
catch (const FileError& e) //not an error in this context
{
- callback.reportInfo(e.toString()); //may throw!
+ callback.reportInfo(e.toString()); //throw X
}
//prevent operating system going into sleep state
@@ -866,7 +1000,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
}
catch (const FileError& e) //not an error in this context
{
- callback.reportInfo(e.toString()); //may throw!
+ callback.reportInfo(e.toString()); //throw X
}
const ResolvedBaseFolders& resInfo = initializeBaseFolders(fpCfgList, deviceParallelOps, folderAccessTimeout, allowUserInteraction, callback);
@@ -978,7 +1112,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
if (!outputByContent.empty())
{
output.push_back(outputByContent.front());
- outputByContent.pop_front();
+ /**/ outputByContent.pop_front();
}
break;
}
@@ -991,7 +1125,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
{
const FolderPairCfg& fpCfg = fpCfgList[it - output.begin()];
- callback.reportStatus(_("Calculating sync directions..."));
+ callback.reportStatus(_("Calculating sync directions...")); //throw X
callback.forceUiRefresh(); //throw X
tryReportingError([&]
diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/base/comparison.h
index 4e9e7b5a..356dbe89 100755
--- a/FreeFileSync/Source/comparison.h
+++ b/FreeFileSync/Source/base/comparison.h
@@ -8,10 +8,10 @@
#define COMPARISON_H_8032178534545426
#include "file_hierarchy.h"
-#include "lib/process_xml.h"
+#include "process_xml.h"
#include "process_callback.h"
-#include "lib/norm_filter.h"
-#include "lib/lock_holder.h"
+#include "norm_filter.h"
+#include "lock_holder.h"
namespace fff
diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/base/db_file.cpp
index 6b13014d..5a134e29 100755
--- a/FreeFileSync/Source/lib/db_file.cpp
+++ b/FreeFileSync/Source/base/db_file.cpp
@@ -272,7 +272,7 @@ private:
{
writeNumber<std::int64_t>(streamOut, descr.modTime);
writeContainer(streamOut, descr.fileId);
- static_assert(IsSameType<decltype(descr.fileId), Zbase<char>>::value, "");
+ static_assert(std::is_same_v<decltype(descr.fileId), Zbase<char>>);
}
static void writeLinkDescr(MemoryStreamOut<ByteArray>& streamOut, const InSyncDescrLink& descr)
@@ -560,7 +560,11 @@ private:
template <class M, class V>
static V& mapAddOrUpdate(M& map, const Zstring& key, V&& value)
{
+#if defined ZEN_LINUX && !defined __cpp_lib_map_try_emplace
auto rv = map.emplace(key, value); //C++11 emplace will move r-value arguments => don't use std::forward!
+#else //C++17's map::try_emplace() is faster than map::emplace() if key is already existing
+ auto rv = map.try_emplace(key, std::forward<V>(value)); //and does NOT MOVE r-value arguments in this case!
+#endif
if (rv.second)
return rv.first->second;
diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/base/db_file.h
index df61d892..17409c2b 100755
--- a/FreeFileSync/Source/lib/db_file.h
+++ b/FreeFileSync/Source/base/db_file.h
@@ -8,7 +8,7 @@
#define DB_FILE_H_834275398588021574
#include <zen/file_error.h>
-#include "../file_hierarchy.h"
+#include "file_hierarchy.h"
namespace fff
diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/base/dir_exist_async.h
index 4daee747..cc5aa479 100755
--- a/FreeFileSync/Source/lib/dir_exist_async.h
+++ b/FreeFileSync/Source/base/dir_exist_async.h
@@ -7,12 +7,11 @@
#ifndef DIR_EXIST_ASYNC_H_0817328167343215806734213
#define DIR_EXIST_ASYNC_H_0817328167343215806734213
-#include <list>
#include <zen/thread.h>
#include <zen/file_error.h>
#include <zen/basic_math.h>
+#include "process_callback.h"
#include "../fs/abstract.h"
-#include "../process_callback.h"
namespace fff
@@ -32,7 +31,7 @@ struct FolderStatus
FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AbstractPath, size_t>& deviceParallelOps,
int folderAccessTimeout, bool allowUserInteraction,
- ProcessCallback& procCallback)
+ ProcessCallback& procCallback) //throw X
{
using namespace zen;
@@ -42,21 +41,20 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
for (const AbstractPath& folderPath : folderPaths)
if (!AFS::isNullPath(folderPath)) //skip empty dirs
perDevicePaths[AFS::getPathComponents(folderPath).rootPath].insert(folderPath);
- warn_static("relax for native paths? consider hanging network share!?")
- std::list<std::pair<AbstractPath, std::future<bool>>> futureInfo;
+ std::vector<std::pair<AbstractPath, std::future<bool>>> futureInfo;
- std::list<ThreadGroup<std::packaged_task<bool()>>> perDeviceThreads;
+ std::vector<ThreadGroup<std::packaged_task<bool()>>> perDeviceThreads;
for (const auto& item : perDevicePaths)
{
const AbstractPath& rootPath = item.first;
auto itParOps = deviceParallelOps.find(rootPath);
const size_t parallelOps = std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1);
- const size_t threadCount = std::min(parallelOps, item.second.size());
- perDeviceThreads.emplace_back(threadCount, "DirExist Device: " + utfTo<std::string>(AFS::getDisplayPath(rootPath)));
+ perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo<std::string>(AFS::getDisplayPath(rootPath)));
auto& threadGroup = perDeviceThreads.back();
+ threadGroup.detach(); //don't wait on threads hanging longer than "folderAccessTimeout"
for (const AbstractPath& folderPath : item.second)
{
@@ -67,7 +65,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
//2. check dir existence
return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError
- //TODO: consider ItemType:FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists
+ //TODO: consider ItemType::FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists
});
auto fut = pt.get_future();
threadGroup.run(std::move(pt));
@@ -85,11 +83,11 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
{
const std::wstring& displayPathFmt = fmtPath(AFS::getDisplayPath(fi.first));
- procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //may throw!
+ procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //throw X
while (numeric::dist(std::chrono::steady_clock::now(), startTime) < std::chrono::seconds(folderAccessTimeout) && //handle potential chrono wrap-around!
fi.second.wait_for(UI_UPDATE_INTERVAL / 2) != std::future_status::ready)
- procCallback.requestUiRefresh(); //may throw!
+ procCallback.requestUiRefresh(); //throw X
if (isReady(fi.second))
{
diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp
index 68ce72f4..b2e30e40 100755
--- a/FreeFileSync/Source/lib/dir_lock.cpp
+++ b/FreeFileSync/Source/base/dir_lock.cpp
@@ -13,8 +13,8 @@
#include <zen/file_access.h>
#include <zen/file_io.h>
#include <zen/optional.h>
-#include <wx/log.h>
-#include <wx/app.h>
+//#include <wx/log.h>
+//#include <wx/app.h>
#include <fcntl.h> //open()
#include <sys/stat.h> //
@@ -36,7 +36,6 @@ const char LOCK_FORMAT_DESCR[] = "FreeFileSync";
const int LOCK_FORMAT_VER = 2; //lock file format version
-
//worker thread
class LifeSigns
{
@@ -49,26 +48,17 @@ public:
{
const Opt<Zstring> parentDirPath = getParentFolderPath(lockFilePath_);
setCurrentThreadName(("DirLock: " + (parentDirPath ? utfTo<std::string>(*parentDirPath) : "")).c_str());
- warn_static("set nice name for setCurrentThreadName in other places, too")
- try
- {
- for (;;)
- {
- interruptibleSleep(EMIT_LIFE_SIGN_INTERVAL); //throw ThreadInterruption
- //actual work
- emitLifeSign(); //throw ()
- }
- }
- catch (const std::exception& e) //exceptions must be catched per thread
+ for (;;)
{
- const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
- wxSafeShowMessage(title, utfTo<wxString>(e.what()) + L" (Dirlock)"); //simple wxMessageBox won't do for threads
+ interruptibleSleep(EMIT_LIFE_SIGN_INTERVAL); //throw ThreadInterruption
+ emitLifeSign(); //noexcept
}
}
private:
- void emitLifeSign() const //try to append one byte..., throw()
+ //try to append one byte...
+ void emitLifeSign() const //noexcept
{
const int fileHandle = ::open(lockFilePath_.c_str(), O_WRONLY | O_APPEND);
if (fileHandle == -1)
@@ -195,8 +185,8 @@ void serialize(const LockInformation& lockInfo, MemoryStreamOut<ByteArray>& stre
writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR));
writeNumber<int32_t>(stream, LOCK_FORMAT_VER);
- static_assert(sizeof(lockInfo.processId) <= sizeof(uint64_t), ""); //ensure cross-platform compatibility!
- static_assert(sizeof(lockInfo.sessionId) <= sizeof(uint64_t), ""); //
+ static_assert(sizeof(lockInfo.processId) <= sizeof(uint64_t)); //ensure cross-platform compatibility!
+ static_assert(sizeof(lockInfo.sessionId) <= sizeof(uint64_t)); //
writeContainer(stream, lockInfo.lockId);
writeContainer(stream, lockInfo.computerName);
@@ -327,7 +317,7 @@ void waitOnDirLock(const Zstring& lockFilePath, const DirLockCallback& notifySta
//one signal missed: it's likely this is an abandoned lock => show countdown
if (lastCheckTime >= lastLifeSign + EMIT_LIFE_SIGN_INTERVAL + std::chrono::seconds(1))
{
- const int remainingSeconds = std::max<int>(0, std::chrono::duration_cast<std::chrono::seconds>(DETECT_ABANDONED_INTERVAL - (now - lastLifeSign)).count());
+ const int remainingSeconds = std::max(0, static_cast<int>(std::chrono::duration_cast<std::chrono::seconds>(DETECT_ABANDONED_INTERVAL - (now - lastLifeSign)).count()));
notifyStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + _P("1 sec", "%x sec", remainingSeconds)); //throw X
}
else
@@ -431,7 +421,7 @@ public:
//create or retrieve a SharedDirLock
std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockFilePath, const DirLockCallback& notifyStatus, std::chrono::milliseconds cbInterval) //throw FileError
{
- assert(std::this_thread::get_id() == mainThreadId); //function is not thread-safe!
+ assert(runningMainThread()); //function is not thread-safe!
tidyUp();
diff --git a/FreeFileSync/Source/lib/dir_lock.h b/FreeFileSync/Source/base/dir_lock.h
index 910e551d..910e551d 100755
--- a/FreeFileSync/Source/lib/dir_lock.h
+++ b/FreeFileSync/Source/base/dir_lock.h
diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/base/error_log.h
index 022bf836..022bf836 100755
--- a/FreeFileSync/Source/lib/error_log.h
+++ b/FreeFileSync/Source/base/error_log.h
diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/base/ffs_paths.cpp
index a9c43d36..a9c43d36 100755
--- a/FreeFileSync/Source/lib/ffs_paths.cpp
+++ b/FreeFileSync/Source/base/ffs_paths.cpp
diff --git a/FreeFileSync/Source/lib/ffs_paths.h b/FreeFileSync/Source/base/ffs_paths.h
index 3cb4c07b..3cb4c07b 100755
--- a/FreeFileSync/Source/lib/ffs_paths.h
+++ b/FreeFileSync/Source/base/ffs_paths.h
diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp
index fd0e8beb..a10646be 100755
--- a/FreeFileSync/Source/file_hierarchy.cpp
+++ b/FreeFileSync/Source/base/file_hierarchy.cpp
@@ -13,8 +13,6 @@ using namespace zen;
using namespace fff;
-
-
std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR)
{
Zstring commonTrail;
@@ -194,7 +192,7 @@ SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir) con
SyncOperation FileSystemObject::getSyncOperation() const
{
- return getIsolatedSyncOperation(!isEmpty<LEFT_SIDE>(), !isEmpty<RIGHT_SIDE>(), getCategory(), selectedForSync_, getSyncDir(), syncDirectionConflict_.get() != nullptr);
+ return getIsolatedSyncOperation(!isEmpty<LEFT_SIDE>(), !isEmpty<RIGHT_SIDE>(), getCategory(), selectedForSync_, getSyncDir(), !syncDirectionConflict_.empty());
//do *not* make a virtual call to testSyncOperation()! See FilePair::testSyncOperation()! <- better not implement one in terms of the other!!!
}
diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/base/file_hierarchy.h
index 482cf50a..f0a61f99 100755
--- a/FreeFileSync/Source/file_hierarchy.h
+++ b/FreeFileSync/Source/base/file_hierarchy.h
@@ -8,18 +8,18 @@
#define FILE_HIERARCHY_H_257235289645296
#include <map>
-#include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t
+//#include <cstddef> //required by GCC 4.9 to find ptrdiff_t
#include <string>
#include <memory>
+#include <list>
#include <functional>
#include <unordered_set>
#include <zen/zstring.h>
-#include <zen/fixed_list.h>
#include <zen/stl_tools.h>
#include <zen/file_id_def.h>
#include "structures.h"
-#include "lib/hard_filter.h"
-#include "fs/abstract.h"
+#include "hard_filter.h"
+#include "../fs/abstract.h"
namespace fff
@@ -36,7 +36,7 @@ struct FileAttributes
fileId(idIn),
isFollowedSymlink(isSymlink)
{
- static_assert(std::is_signed<time_t>::value, "... and signed!");
+ static_assert(std::is_signed_v<time_t>, "... and signed!");
}
time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC
@@ -75,10 +75,10 @@ template <SelectedSide side>
struct OtherSide;
template <>
-struct OtherSide<LEFT_SIDE> { static const SelectedSide result = RIGHT_SIDE; };
+struct OtherSide<LEFT_SIDE> { static const SelectedSide value = RIGHT_SIDE; };
template <>
-struct OtherSide<RIGHT_SIDE> { static const SelectedSide result = LEFT_SIDE; };
+struct OtherSide<RIGHT_SIDE> { static const SelectedSide value = LEFT_SIDE; };
template <SelectedSide side>
@@ -201,9 +201,9 @@ class ContainerObject : public virtual PathInformation
friend class FileSystemObject;
public:
- using FileList = zen::FixedList<FilePair>; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back()
- using SymlinkList = zen::FixedList<SymlinkPair>; //
- using FolderList = zen::FixedList<FolderPair>;
+ using FileList = std::list<FilePair>; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back()
+ using SymlinkList = std::list<SymlinkPair>; //
+ using FolderList = std::list<FolderPair>;
FolderPair& addSubFolder(const Zstring& itemNameL,
const FolderAttributes& left, //file exists on both sides
@@ -332,10 +332,16 @@ private:
//get rid of shared_ptr indirection
template <class IterImpl, //underlying iterator type
- class V> //target value type
-class DerefIter : public std::iterator<std::bidirectional_iterator_tag, V>
+ class T> //target value type
+class DerefIter
{
public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+
DerefIter() {}
DerefIter(IterImpl it) : it_(it) {}
DerefIter(const DerefIter& other) : it_(other.it_) {}
@@ -346,24 +352,12 @@ public:
inline friend ptrdiff_t operator-(const DerefIter& lhs, const DerefIter& rhs) { return lhs.it_ - rhs.it_; }
inline friend bool operator==(const DerefIter& lhs, const DerefIter& rhs) { return lhs.it_ == rhs.it_; }
inline friend bool operator!=(const DerefIter& lhs, const DerefIter& rhs) { return !(lhs == rhs); }
- V& operator* () const { return **it_; }
- V* operator->() const { return &** it_; }
+ T& operator* () const { return **it_; }
+ T* operator->() const { return &** it_; }
private:
IterImpl it_{};
};
-/*
-C++17: specialize std::iterator_traits instead of inherting from std::iterator
-namespace std
-{
-template <class IterImpl, class V>
-struct iterator_traits<zen::DerefIter<IterImpl, V>>
-{
- using iterator_category = std::bidirectional_iterator_tag;
- using value_type = V;
-};
-}
-*/
using FolderComparison = std::vector<std::shared_ptr<BaseFolderPair>>; //make sure pointers to sub-elements remain valid
//don't change this back to std::vector<BaseFolderPair> too easily: comparison uses push_back to add entries which may result in a full copy!
@@ -383,8 +377,6 @@ struct FSObjectVisitor
};
-
-
//inherit from this class to allow safe random access by id instead of unsafe raw pointer
//allow for similar semantics like std::weak_ptr without having to use std::shared_ptr
template <class T>
@@ -415,7 +407,7 @@ private:
static std::unordered_set<const ObjectMgr*>& activeObjects()
{
//our global ObjectMgr is not thread-safe (and currently does not need to be!)
- //assert(std::this_thread::get_id() == mainThreadId); -> still, may be accessed by synchronization worker thread, one-at-a-time
+ //assert(runningMainThread()); -> still, may be accessed by synchronization worker threads, one thread at a time
static std::unordered_set<const ObjectMgr*> inst;
return inst; //external linkage (even in header file!)
@@ -462,8 +454,8 @@ public:
//for use during init in "CompareProcess" only:
template <CompareFilesResult res> void setCategory();
- void setCategoryConflict (const std::wstring& description);
- void setCategoryDiffMetadata(const std::wstring& description);
+ void setCategoryConflict (const Zstringw& description);
+ void setCategoryDiffMetadata(const Zstringw& description);
protected:
FileSystemObject(const Zstring& itemNameL,
@@ -501,14 +493,14 @@ private:
void propagateChangedItemName(const Zstring& itemNameOld); //required after any itemName changes
//categorization
- std::unique_ptr<std::wstring> cmpResultDescr_; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA
+ Zstringw cmpResultDescr_; //only filled if getCategory() == FILE_CONFLICT or FILE_DIFFERENT_METADATA
CompareFilesResult cmpResult_; //although this uses 4 bytes there is currently *no* space wasted in class layout!
bool selectedForSync_ = true;
//Note: we model *four* states with following two variables => "syncDirectionConflict is empty or syncDir == NONE" is a class invariant!!!
SyncDirection syncDir_ = SyncDirection::NONE; //1 byte: optimize memory layout!
- std::unique_ptr<std::wstring> syncDirectionConflict_; //non-empty if we have a conflict setting sync-direction
+ Zstringw syncDirectionConflict_; //non-empty if we have a conflict setting sync-direction
//get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!)
Zstring itemNameL_; //slightly redundant under Linux, but on Windows the "same" file paths can differ in case
@@ -739,9 +731,7 @@ inline
std::wstring FileSystemObject::getCatExtraDescription() const
{
assert(getCategory() == FILE_CONFLICT || getCategory() == FILE_DIFFERENT_METADATA);
- if (cmpResultDescr_) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
- return *cmpResultDescr_;
- return std::wstring();
+ return zen::copyStringTo<std::wstring>(cmpResultDescr_);
}
@@ -749,7 +739,7 @@ inline
void FileSystemObject::setSyncDir(SyncDirection newDir)
{
syncDir_ = newDir;
- syncDirectionConflict_.reset();
+ syncDirectionConflict_.clear();
notifySyncCfgChanged();
}
@@ -758,8 +748,9 @@ void FileSystemObject::setSyncDir(SyncDirection newDir)
inline
void FileSystemObject::setSyncDirConflict(const std::wstring& description)
{
+ assert(!description.empty());
syncDir_ = SyncDirection::NONE;
- syncDirectionConflict_ = std::make_unique<std::wstring>(description);
+ syncDirectionConflict_ = zen::copyStringTo<Zstringw>(description);
notifySyncCfgChanged();
}
@@ -769,9 +760,7 @@ inline
std::wstring FileSystemObject::getSyncOpConflict() const
{
assert(getSyncOperation() == SO_UNRESOLVED_CONFLICT);
- if (syncDirectionConflict_) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
- return *syncDirectionConflict_;
- return std::wstring();
+ return zen::copyStringTo<std::wstring>(syncDirectionConflict_);
}
@@ -805,7 +794,7 @@ Zstring FileSystemObject::getItemName() const
const Zstring& itemName = SelectParam<side>::ref(itemNameL_, itemNameR_); //empty if not existing
if (!itemName.empty()) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
return itemName;
- return SelectParam<OtherSide<side>::result>::ref(itemNameL_, itemNameR_); //empty if not existing
+ return SelectParam<OtherSide<side>::value>::ref(itemNameL_, itemNameR_); //empty if not existing
}
@@ -871,17 +860,19 @@ template <> void FileSystemObject::setCategory<FILE_LEFT_SIDE_ONLY> () = dele
template <> void FileSystemObject::setCategory<FILE_RIGHT_SIDE_ONLY> () = delete; //
inline
-void FileSystemObject::setCategoryConflict(const std::wstring& description)
+void FileSystemObject::setCategoryConflict(const Zstringw& description)
{
+ assert(!description.empty());
cmpResult_ = FILE_CONFLICT;
- cmpResultDescr_ = std::make_unique<std::wstring>(description);
+ cmpResultDescr_ = zen::copyStringTo<Zstringw>(description);
}
inline
-void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description)
+void FileSystemObject::setCategoryDiffMetadata(const Zstringw& description)
{
+ assert(!description.empty());
cmpResult_ = FILE_DIFFERENT_METADATA;
- cmpResultDescr_ = std::make_unique<std::wstring>(description);
+ cmpResultDescr_ = zen::copyStringTo<Zstringw>(description);
}
inline
@@ -1173,7 +1164,7 @@ void FilePair::setSyncedTo(const Zstring& itemName,
bool isSymlinkSrc)
{
//FILE_EQUAL is only allowed for same short name and file size: enforced by this method!
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
SelectParam<sideTrg>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg);
SelectParam<sideSrc>::ref(attrL_, attrR_) = FileAttributes(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc);
@@ -1188,7 +1179,7 @@ void SymlinkPair::setSyncedTo(const Zstring& itemName,
int64_t lastWriteTimeTrg,
int64_t lastWriteTimeSrc)
{
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
SelectParam<sideTrg>::ref(attrL_, attrR_) = LinkAttributes(lastWriteTimeTrg);
SelectParam<sideSrc>::ref(attrL_, attrR_) = LinkAttributes(lastWriteTimeSrc);
@@ -1202,7 +1193,7 @@ void FolderPair::setSyncedTo(const Zstring& itemName,
bool isSymlinkTrg,
bool isSymlinkSrc)
{
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
SelectParam<sideTrg>::ref(attrL_, attrR_) = FolderAttributes(isSymlinkTrg);
SelectParam<sideSrc>::ref(attrL_, attrR_) = FolderAttributes(isSymlinkSrc);
diff --git a/FreeFileSync/Source/lib/generate_logfile.cpp b/FreeFileSync/Source/base/generate_logfile.cpp
index 2ee7f309..293bedb7 100755
--- a/FreeFileSync/Source/lib/generate_logfile.cpp
+++ b/FreeFileSync/Source/base/generate_logfile.cpp
@@ -217,8 +217,7 @@ void fff::limitLogfileCount(const AbstractPath& logFolderPath, const std::wstrin
//traverse source directory one level deep
auto lt = std::make_shared<LogTraverserCallback>(prefix, [&] { if (notifyStatus) notifyStatus(cleaningMsg); });
- const AFS::PathComponents pc = AFS::getPathComponents(logFolderPath);
- AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, lt }}, 1 /*parallelOps*/); //throw FileError
+ AFS::traverseFolderParallel(logFolderPath, {{ {}, lt }}, 1 /*parallelOps*/); //throw FileError
std::vector<Zstring> logFileNames = lt->refFileNames();
Opt<FileError> lastError = lt->getLastError();
diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/base/generate_logfile.h
index ecd1db1f..40be8d4a 100755
--- a/FreeFileSync/Source/lib/generate_logfile.h
+++ b/FreeFileSync/Source/base/generate_logfile.h
@@ -9,7 +9,7 @@
#include <zen/error_log.h>
#include "ffs_paths.h"
-#include "../file_hierarchy.h"
+#include "file_hierarchy.h"
namespace fff
diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/base/hard_filter.cpp
index 3008aa24..d4085f84 100755
--- a/FreeFileSync/Source/lib/hard_filter.cpp
+++ b/FreeFileSync/Source/base/hard_filter.cpp
@@ -30,7 +30,7 @@ namespace
//constructing Zstrings of these in addFilterEntry becomes perf issue for large filter lists => use global POD!
const Zchar sepAsterisk[] = Zstr("/*");
const Zchar asteriskSep[] = Zstr("*/");
-static_assert(FILE_NAME_SEPARATOR == '/', "");
+static_assert(FILE_NAME_SEPARATOR == '/');
void addFilterEntry(const Zstring& filterPhrase, std::vector<Zstring>& masksFileFolder, std::vector<Zstring>& masksFolder)
diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/base/hard_filter.h
index f829761e..f829761e 100755
--- a/FreeFileSync/Source/lib/hard_filter.h
+++ b/FreeFileSync/Source/base/hard_filter.h
diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/base/help_provider.h
index 1ac0a278..8cee813b 100755
--- a/FreeFileSync/Source/lib/help_provider.h
+++ b/FreeFileSync/Source/base/help_provider.h
@@ -10,7 +10,7 @@
#if 1
namespace fff
{
-inline void displayHelpEntry(const wxString& topic, wxWindow* parent) { wxLaunchDefaultBrowser(L"https://www.freefilesync.org/manual.php?topic=" + topic); }
+inline void displayHelpEntry(const wxString& topic, wxWindow* parent) { wxLaunchDefaultBrowser(L"https://freefilesync.org/manual.php?topic=" + topic); }
inline void uninitializeHelp() {}
}
@@ -42,7 +42,7 @@ inline
void displayHelpEntry(const wxString& topic, wxWindow* parent)
{
if (internetIsAlive()) //noexcept
- wxLaunchDefaultBrowser(L"https://www.freefilesync.org/manual.php?topic=" + topic);
+ wxLaunchDefaultBrowser(L"https://freefilesync.org/manual.php?topic=" + topic);
else
-> what if FFS is blocked, but the web browser would have internet access??
{
diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/base/icon_buffer.cpp
index 15bd6f93..6c030bb5 100755
--- a/FreeFileSync/Source/lib/icon_buffer.cpp
+++ b/FreeFileSync/Source/base/icon_buffer.cpp
@@ -24,11 +24,10 @@ namespace
const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to hold in buffer: must be big enough to hold visible icons + preload buffer! Consider OS limit on GDI resources (wxBitmap)!!!
-
//destroys raw icon! Call from GUI thread only!
wxBitmap extractWxBitmap(ImageHolder&& ih)
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
if (!ih.getRgb())
return wxNullBitmap;
@@ -81,7 +80,7 @@ public:
//context of main thread
void set(const std::vector<AbstractPath>& newLoad)
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
{
std::lock_guard<std::mutex> dummy(lockFiles_);
@@ -90,12 +89,12 @@ public:
workLoad_.emplace_back(filePath);
}
conditionNewWork_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
- //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
+ //condition handling, see: https://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
}
void add(const AbstractPath& filePath) //context of main thread
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
{
std::lock_guard<std::mutex> dummy(lockFiles_);
workLoad_.emplace_back(filePath); //set as next item to retrieve
@@ -106,7 +105,7 @@ public:
//context of worker thread, blocking:
AbstractPath extractNext() //throw ThreadInterruption
{
- assert(std::this_thread::get_id() != mainThreadId);
+ assert(!runningMainThread());
std::unique_lock<std::mutex> dummy(lockFiles_);
interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty(); }); //throw ThreadInterruption
@@ -137,7 +136,7 @@ public:
//must be called by main thread only! => wxBitmap is NOT thread-safe like an int (non-atomic ref-count!!!)
Opt<wxBitmap> retrieve(const AbstractPath& filePath)
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
std::lock_guard<std::mutex> dummy(lockIconList_);
auto it = iconList.find(filePath);
@@ -161,7 +160,7 @@ public:
std::lock_guard<std::mutex> dummy(lockIconList_);
//thread safety: moving ImageHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here!
- auto rc = iconList.emplace(filePath, makeValueObject());
+ auto rc = iconList.emplace(filePath, IconData());
assert(rc.second); //insertion took place
if (rc.second)
{
@@ -174,7 +173,7 @@ public:
//call at an appropriate time, e.g. after Workload::set()
void limitSize()
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
std::lock_guard<std::mutex> dummy(lockIconList_);
while (iconList.size() > BUFFER_SIZE_MAX)
@@ -187,16 +186,8 @@ public:
private:
struct IconData;
-
-#ifdef __clang__ //workaround libc++ limitation for incomplete types: http://llvm.org/bugs/show_bug.cgi?id=17701
- using FileIconMap = std::map<AbstractPath, std::unique_ptr<IconData>>;
- static IconData& refData(FileIconMap::iterator it) { return *(it->second); }
- static std::unique_ptr<IconData> makeValueObject() { return std::make_unique<IconData>(); }
-#else
using FileIconMap = std::map<AbstractPath, IconData>;
IconData& refData(FileIconMap::iterator it) { return it->second; }
- static IconData makeValueObject() { return IconData(); }
-#endif
//call while holding lock:
void priorityListPopFront()
@@ -290,7 +281,7 @@ struct IconBuffer::Impl
Buffer buffer; //
InterruptibleThread worker;
-
+ //-------------------------
//-------------------------
std::map<Zstring, wxBitmap, LessFilePath> extensionIcons; //no item count limit!? Test case C:\ ~ 3800 unique file extensions
};
@@ -301,14 +292,15 @@ IconBuffer::IconBuffer(IconSize sz) : pimpl_(std::make_unique<Impl>()), iconSize
pimpl_->worker = InterruptibleThread([&workload = pimpl_->workload, &buffer = pimpl_->buffer, sz]
{
setCurrentThreadName("Icon Buffer");
- for (;;)
- {
- //start work: blocks until next icon to load is retrieved:
- const AbstractPath itemPath = workload.extractNext(); //throw ThreadInterruption
- if (!buffer.hasIcon(itemPath)) //perf: workload may contain duplicate entries?
- buffer.insert(itemPath, getDisplayIcon(itemPath, sz));
- }
+ for (;;)
+ {
+ //start work: blocks until next icon to load is retrieved:
+ const AbstractPath itemPath = workload.extractNext(); //throw ThreadInterruption
+
+ if (!buffer.hasIcon(itemPath)) //perf: workload may contain duplicate entries?
+ buffer.insert(itemPath, getDisplayIcon(itemPath, sz));
+ }
});
}
@@ -361,7 +353,7 @@ void IconBuffer::setWorkload(const std::vector<AbstractPath>& load)
assert(load.size() < BUFFER_SIZE_MAX / 2);
pimpl_->workload.set(load); //since buffer can only increase due to new workload,
- pimpl_->buffer.limitSize(); //this is the place to impose the limit from main thread!
+ pimpl_->buffer.limitSize(); //this is the place to impose the limit from main thread!
}
@@ -369,7 +361,7 @@ wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath)
{
const Zstring& ext = getFileExtension(filePath);
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
auto it = pimpl_->extensionIcons.find(ext);
if (it == pimpl_->extensionIcons.end())
@@ -377,7 +369,8 @@ wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath)
const Zstring& templateName(ext.empty() ? Zstr("file") : Zstr("file.") + ext);
//don't pass actual file name to getIconByTemplatePath(), e.g. "AUTHORS" has own mime type on Linux!!!
//=> we want to buffer by extension only to minimize buffer-misses!
- it = pimpl_->extensionIcons.emplace(ext, extractWxBitmap(getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType_)))).first;
+
+ it = pimpl_->extensionIcons.emplace(ext, extractWxBitmap(getIconByTemplatePath(templateName, getSize(iconSizeType_)))).first;
}
//need buffer size limit???
return it->second;
diff --git a/FreeFileSync/Source/lib/icon_buffer.h b/FreeFileSync/Source/base/icon_buffer.h
index e5ff932b..f0ad78e4 100755
--- a/FreeFileSync/Source/lib/icon_buffer.h
+++ b/FreeFileSync/Source/base/icon_buffer.h
@@ -33,11 +33,11 @@ public:
static int getSize(IconSize sz); //expected and *maximum* icon size in pixel
int getSize() const { return getSize(iconSizeType_); } //
- bool readyForRetrieval(const AbstractPath& filePath);
+ void setWorkload (const std::vector<AbstractPath>& load); //(re-)set new workload of icons to be retrieved;
+ bool readyForRetrieval(const AbstractPath& filePath);
zen::Opt<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot
- void setWorkload (const std::vector<AbstractPath>& load); //(re-)set new workload of icons to be retrieved;
-
wxBitmap getIconByExtension(const Zstring& filePath); //...and add to buffer
+ //retrieveFileIcon() + getIconByExtension() are safe to call from within WM_PAINT handler! no COM calls (...on calling thread)
static wxBitmap genericFileIcon(IconSize sz);
static wxBitmap genericDirIcon (IconSize sz);
diff --git a/FreeFileSync/Source/lib/icon_loader.cpp b/FreeFileSync/Source/base/icon_loader.cpp
index 71b783a2..71b783a2 100755
--- a/FreeFileSync/Source/lib/icon_loader.cpp
+++ b/FreeFileSync/Source/base/icon_loader.cpp
diff --git a/FreeFileSync/Source/lib/icon_loader.h b/FreeFileSync/Source/base/icon_loader.h
index 7f14ff54..8f6b31b9 100755
--- a/FreeFileSync/Source/lib/icon_loader.h
+++ b/FreeFileSync/Source/base/icon_loader.h
@@ -15,12 +15,13 @@ namespace fff
{
//=> all functions are safe to call from multiple threads!
//!!!Note: init COM + system image list before loading icons!!!
+//=> don't call from WM_PAINT handler! https://blogs.msdn.microsoft.com/yvesdolc/2009/08/06/do-you-receive-wm_paint-when-waiting-for-a-com-call-to-return/
//return null icon on failure:
zen::ImageHolder getIconByTemplatePath(const Zstring& templatePath, int pixelSize);
zen::ImageHolder genericFileIcon(int pixelSize);
-zen::ImageHolder genericDirIcon(int pixelSize);
-zen::ImageHolder getFileIcon(const Zstring& filePath, int pixelSize);
+zen::ImageHolder genericDirIcon (int pixelSize);
+zen::ImageHolder getFileIcon (const Zstring& filePath, int pixelSize);
zen::ImageHolder getThumbnailImage(const Zstring& filePath, int pixelSize);
}
diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/base/localization.cpp
index bc27e6ea..bc27e6ea 100755
--- a/FreeFileSync/Source/lib/localization.cpp
+++ b/FreeFileSync/Source/base/localization.cpp
diff --git a/FreeFileSync/Source/lib/localization.h b/FreeFileSync/Source/base/localization.h
index bf2f6b60..bf2f6b60 100755
--- a/FreeFileSync/Source/lib/localization.h
+++ b/FreeFileSync/Source/base/localization.h
diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/base/lock_holder.h
index 128f4b8e..cfe6a4fb 100755
--- a/FreeFileSync/Source/lib/lock_holder.h
+++ b/FreeFileSync/Source/base/lock_holder.h
@@ -46,7 +46,7 @@ public:
msg += L"\n" + replaceCpy(fl.second.toString(), L"\n\n", L"\n");
}
- pcb.reportWarning(msg, warnDirectoryLockFailed); //may throw!
+ pcb.reportWarning(msg, warnDirectoryLockFailed); //throw X
}
}
diff --git a/FreeFileSync/Source/lib/norm_filter.h b/FreeFileSync/Source/base/norm_filter.h
index c2606ffc..c2606ffc 100755
--- a/FreeFileSync/Source/lib/norm_filter.h
+++ b/FreeFileSync/Source/base/norm_filter.h
diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/base/parallel_scan.cpp
index 954b6b07..7f356ee7 100755
--- a/FreeFileSync/Source/lib/parallel_scan.cpp
+++ b/FreeFileSync/Source/base/parallel_scan.cpp
@@ -10,7 +10,6 @@
#include <zen/basic_math.h>
#include <zen/thread.h>
#include <zen/scope_guard.h>
-#include <zen/fixed_list.h>
#include "db_file.h"
#include "lock_holder.h"
@@ -20,7 +19,6 @@ using namespace fff;
namespace
{
-
/*
#ifdef ZEN_WIN
@@ -90,7 +88,7 @@ DiskInfo retrieveDiskInfo(const Zstring& itemPath)
return output;
ZEN_ON_SCOPE_EXIT(::CloseHandle(hVolume));
- std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks!
+ std::vector<std::byte> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks!
DWORD bytesReturned = 0;
if (!::DeviceIoControl(hVolume, //_In_ HANDLE hDevice,
@@ -163,7 +161,7 @@ public:
//blocking call: context of worker thread
FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption
{
- assert(std::this_thread::get_id() != mainThreadId);
+ assert(!runningMainThread());
std::unique_lock<std::mutex> dummy(lockRequest_);
interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !errorRequest_ && !errorResponse_; }); //throw ThreadInterruption
@@ -186,7 +184,7 @@ public:
//context of main thread
void waitUntilDone(std::chrono::milliseconds duration, FillBufferCallback& callback) //throw X
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
for (;;)
{
const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
@@ -235,7 +233,7 @@ public:
void reportCurrentFile(const std::wstring& filePath) //context of worker thread
{
- assert(std::this_thread::get_id() != mainThreadId);
+ assert(!runningMainThread());
std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
currentFile_ = filePath;
}
@@ -275,7 +273,7 @@ public:
private:
std::wstring getCurrentStatus() //context of main thread, call repreatedly
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
size_t parallelOpsTotal = 0;
std::wstring filePath;
@@ -287,12 +285,10 @@ private:
filePath = currentFile_;
}
-
- std::wstring output = textScanning_;
if (parallelOpsTotal >= 2)
- output += L"[" + _P("1 thread", "%x threads", parallelOpsTotal) + L"] ";
- output += filePath;
- return output;
+ return textScanning_ + L"[" + _P("1 thread", "%x threads", parallelOpsTotal) + L"] " + filePath;
+ else
+ return textScanning_ + filePath;
}
//---- main <-> worker communication channel ----
@@ -302,10 +298,11 @@ private:
std::condition_variable conditionHaveResponse_;
Opt<std::pair<std::wstring, size_t>> errorRequest_; //error message + retry number
Opt<FillBufferCallback::HandleError> errorResponse_;
- size_t threadsToFinish_; //!= activeThreadIdxs_.size(), which may be 0 during worker thread construction!
+ size_t threadsToFinish_; //can't use activeThreadIdxs_.size() which is locked by different mutex!
+ //also note: activeThreadIdxs_.size() may be 0 during worker thread construction!
//---- status updates ----
- std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error
+ std::mutex lockCurrentStatus_; //different lock for status updates so that we're not blocked by other threads reporting errors
std::wstring currentFile_;
std::map<int /*threadIdx*/, size_t /*parallelOps*/> activeThreadIdxs_;
@@ -350,10 +347,12 @@ public:
virtual std::shared_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override; //throw ThreadInterruption
virtual HandleLink onSymlink(const SymlinkInfo& li) override; //
- HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override; //throw ThreadInterruption
- HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override; //
+ HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { return reportError(msg, retryNumber, Zstring()); } //throw ThreadInterruption
+ HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { return reportError(msg, retryNumber, itemName); } //
private:
+ HandleError reportError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName /*optional*/); //throw ThreadInterruption
+
TraverserConfig& cfg_;
const Zstring parentRelPathPf_;
FolderContainer& output_;
@@ -450,11 +449,15 @@ std::shared_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo&
//------------------------------------------------------------------------------------
if (level_ > 100) //Win32 traverser: stack overflow approximately at level 1000
//check after FolderContainer::addSubFolder()
- if (!tryReportingItemError([&] //throw ThreadInterruption
- {
- throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, folderRelPath))), L"Endless recursion.");
- }, *this, fi.itemName))
- return nullptr;
+ for (size_t retryNumber = 0;; ++retryNumber)
+ switch (reportItemError(replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg_.baseFolderPath, folderRelPath))) +
+ L"\n\n" L"Endless recursion.", retryNumber, fi.itemName)) //throw ThreadInterruption
+ {
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ break;
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ return nullptr;
+ }
return std::make_shared<DirCallback>(cfg_, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1);
}
@@ -501,28 +504,15 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th
}
-DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption
-{
- switch (cfg_.acb.reportError(msg, retryNumber)) //throw ThreadInterruption
- {
- case FillBufferCallback::ON_ERROR_CONTINUE:
- cfg_.failedDirReads[beforeLast(parentRelPathPf_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)] = msg;
- return ON_ERROR_CONTINUE;
-
- case FillBufferCallback::ON_ERROR_RETRY:
- return ON_ERROR_RETRY;
- }
- assert(false);
- return ON_ERROR_CONTINUE;
-}
-
-
-DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) //throw ThreadInterruption
+DirCallback::HandleError DirCallback::reportError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) //throw ThreadInterruption
{
switch (cfg_.acb.reportError(msg, retryNumber)) //throw ThreadInterruption
{
case FillBufferCallback::ON_ERROR_CONTINUE:
- cfg_.failedItemReads[parentRelPathPf_ + itemName] = msg;
+ if (itemName.empty())
+ cfg_.failedDirReads.emplace(beforeLast(parentRelPathPf_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE), msg);
+ else
+ cfg_.failedItemReads.emplace(parentRelPathPf_ + itemName, msg);
return ON_ERROR_CONTINUE;
case FillBufferCallback::ON_ERROR_RETRY:
@@ -554,7 +544,7 @@ void fff::fillBuffer(const std::set<DirectoryKey>& foldersToRead, //in
//communication channel used by threads
AsyncCallback acb(perDeviceFolders.size() /*threadsToFinish*/, cbInterval); //manage life time: enclose InterruptibleThread's!!!
- FixedList<InterruptibleThread> worker;
+ std::vector<InterruptibleThread> worker;
ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.join(); );
ZEN_ON_SCOPE_FAIL( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join
@@ -564,12 +554,9 @@ void fff::fillBuffer(const std::set<DirectoryKey>& foldersToRead, //in
const AbstractPath& rootPath = item.first;
const int threadIdx = static_cast<int>(worker.size());
-#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
auto itParOps = deviceParallelOps.find(rootPath);
const size_t parallelOps = std::max<size_t>(itParOps != deviceParallelOps.end() ? itParOps->second : 1, 1); //sanitize early for correct status display
-#elif defined ZEN_LINUX && defined ZEN_LINUX_TRAVERSER_LEGACY
- const size_t parallelOps = 1;
-#endif
+
std::map<DirectoryKey, DirectoryValue*> workload;
for (const DirectoryKey& key : item.second)
@@ -577,7 +564,7 @@ void fff::fillBuffer(const std::set<DirectoryKey>& foldersToRead, //in
worker.emplace_back([rootPath, workload, threadIdx, &acb, parallelOps]() mutable
{
- setCurrentThreadName(("Traverser[" + numberTo<std::string>(threadIdx) + "]").c_str());
+ setCurrentThreadName(("Comp Worker[" + numberTo<std::string>(threadIdx) + "]").c_str());
acb.notifyWorkBegin(threadIdx, parallelOps);
ZEN_ON_SCOPE_EXIT(acb.notifyWorkEnd(threadIdx));
diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/base/parallel_scan.h
index 7e54428a..f3f3e597 100755
--- a/FreeFileSync/Source/lib/parallel_scan.h
+++ b/FreeFileSync/Source/base/parallel_scan.h
@@ -11,8 +11,8 @@
#include <set>
#include <chrono>
#include "hard_filter.h"
-#include "../structures.h"
-#include "../file_hierarchy.h"
+#include "structures.h"
+#include "file_hierarchy.h"
namespace fff
@@ -60,7 +60,7 @@ struct FillBufferCallback
ON_ERROR_RETRY,
ON_ERROR_CONTINUE
};
- virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //may throw!
+ virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //throw X
virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; //
};
diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/base/parse_lng.h
index b6d486e2..b6d486e2 100755
--- a/FreeFileSync/Source/lib/parse_lng.h
+++ b/FreeFileSync/Source/base/parse_lng.h
diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/base/parse_plural.h
index 8a9173e3..8a9173e3 100755
--- a/FreeFileSync/Source/lib/parse_plural.h
+++ b/FreeFileSync/Source/base/parse_plural.h
diff --git a/FreeFileSync/Source/lib/perf_check.cpp b/FreeFileSync/Source/base/perf_check.cpp
index 8b6f52d6..ae4a8cc8 100755
--- a/FreeFileSync/Source/lib/perf_check.cpp
+++ b/FreeFileSync/Source/base/perf_check.cpp
@@ -45,8 +45,7 @@ std::tuple<double /*timeDelta*/, int /*itemsDelta*/, double /*bytesDelta*/> Perf
const int itemsDelta = itBack->second.items - itFront->second.items;
const double bytesDelta = itBack->second.bytes - itFront->second.bytes;
- //return { timeDelta, itemsDelta, bytesDelta }; -> requires C++17 (Linux-only issue)
- return std::make_tuple(timeDelta, itemsDelta, bytesDelta);
+ return { timeDelta, itemsDelta, bytesDelta };
}
diff --git a/FreeFileSync/Source/lib/perf_check.h b/FreeFileSync/Source/base/perf_check.h
index 401d08f5..401d08f5 100755
--- a/FreeFileSync/Source/lib/perf_check.h
+++ b/FreeFileSync/Source/base/perf_check.h
diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/base/process_callback.h
index 0a8f487e..0a8f487e 100755
--- a/FreeFileSync/Source/process_callback.h
+++ b/FreeFileSync/Source/base/process_callback.h
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/base/process_xml.cpp
index 95cac76e..95cac76e 100755
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/base/process_xml.cpp
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/base/process_xml.h
index 13709ec6..1c9a3d72 100755
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/base/process_xml.h
@@ -10,7 +10,7 @@
#include <zen/xml_io.h>
#include <wx/gdicmn.h>
#include "localization.h"
-#include "../structures.h"
+#include "structures.h"
#include "../ui/file_grid_attr.h"
#include "../ui/tree_grid_attr.h" //RTS: avoid tree grid's "file_hierarchy.h" dependency!
#include "../ui/cfg_grid.h"
@@ -192,8 +192,8 @@ struct XmlGlobalSettings
size_t lastSyncsLogFileSizeMax = 100000; //maximum size for LastSyncs.log: use a human-readable number
Zstring soundFileCompareFinished;
Zstring soundFileSyncFinished = Zstr("gong.wav");
- bool autoCloseProgressDialog = false;
+ bool autoCloseProgressDialog = false;
ConfirmationDialogs confirmDlgs;
WarningDialogs warnDlgs;
diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/base/resolve_path.cpp
index 7a17422b..b40b91ce 100755
--- a/FreeFileSync/Source/lib/resolve_path.cpp
+++ b/FreeFileSync/Source/base/resolve_path.cpp
@@ -17,11 +17,9 @@ using namespace zen;
namespace
{
-
-
Opt<Zstring> getEnvironmentVar(const Zstring& name)
{
- assert(std::this_thread::get_id() == mainThreadId); //getenv() is not thread-safe!
+ assert(runningMainThread()); //getenv() is not thread-safe!
const char* buffer = ::getenv(name.c_str()); //no extended error reporting
if (!buffer)
@@ -43,7 +41,7 @@ Opt<Zstring> getEnvironmentVar(const Zstring& name)
Zstring resolveRelativePath(const Zstring& relativePath)
{
- assert(std::this_thread::get_id() == mainThreadId); //GetFullPathName() is documented to NOT be thread-safe!
+ assert(runningMainThread()); //GetFullPathName() is documented to NOT be thread-safe!
//http://linux.die.net/man/2/path_resolution
if (!startsWith(relativePath, FILE_NAME_SEPARATOR)) //absolute names are exactly those starting with a '/'
diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/base/resolve_path.h
index dc23d1de..dc23d1de 100755
--- a/FreeFileSync/Source/lib/resolve_path.h
+++ b/FreeFileSync/Source/base/resolve_path.h
diff --git a/FreeFileSync/Source/lib/return_codes.h b/FreeFileSync/Source/base/return_codes.h
index 9604142c..9604142c 100755
--- a/FreeFileSync/Source/lib/return_codes.h
+++ b/FreeFileSync/Source/base/return_codes.h
diff --git a/FreeFileSync/Source/lib/soft_filter.h b/FreeFileSync/Source/base/soft_filter.h
index f0b7eec8..d0552c2d 100755
--- a/FreeFileSync/Source/lib/soft_filter.h
+++ b/FreeFileSync/Source/base/soft_filter.h
@@ -9,7 +9,7 @@
#include <algorithm>
#include <limits>
-#include "../structures.h"
+#include "structures.h"
namespace fff
diff --git a/FreeFileSync/Source/lib/status_handler.cpp b/FreeFileSync/Source/base/status_handler.cpp
index 9e2f78db..9e2f78db 100755
--- a/FreeFileSync/Source/lib/status_handler.cpp
+++ b/FreeFileSync/Source/base/status_handler.cpp
diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/base/status_handler.h
index c84396ec..a5cbab86 100755
--- a/FreeFileSync/Source/lib/status_handler.h
+++ b/FreeFileSync/Source/base/status_handler.h
@@ -7,13 +7,13 @@
#ifndef STATUS_HANDLER_H_81704805908341534
#define STATUS_HANDLER_H_81704805908341534
-#include "../process_callback.h"
#include <vector>
#include <chrono>
#include <thread>
#include <string>
#include <zen/i18n.h>
#include <zen/basic_math.h>
+#include "process_callback.h"
namespace fff
@@ -71,7 +71,7 @@ public:
numbersTotal_ (4) {} //
//implement parts of ProcessCallback
- void initNewPhase(int itemsTotal, int64_t bytesTotal, Phase phaseId) override //may throw
+ void initNewPhase(int itemsTotal, int64_t bytesTotal, Phase phaseId) override //(throw X)
{
currentPhase_ = phaseId;
refNumbers(numbersTotal_, currentPhase_) = { itemsTotal, bytesTotal };
@@ -107,8 +107,7 @@ public:
void reportStatus(const std::wstring& text) override final //throw X
{
- warn_static("std::wstring statusText_: perf issue?")
- //assert(!text.empty()); -> possible: start of parallel scan
+ //assert(!text.empty()); -> possible, e.g. start of parallel scan
statusText_ = text; //update text *before* running operations that can throw
requestUiRefresh(); //throw X
}
diff --git a/FreeFileSync/Source/base/status_handler_impl.h b/FreeFileSync/Source/base/status_handler_impl.h
new file mode 100755
index 00000000..02b190c8
--- /dev/null
+++ b/FreeFileSync/Source/base/status_handler_impl.h
@@ -0,0 +1,386 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef STATUS_HANDLER_IMPL_H_07682758976
+#define STATUS_HANDLER_IMPL_H_07682758976
+
+#include <zen/optional.h>
+#include <zen/file_error.h>
+#include <zen/thread.h>
+#include "process_callback.h"
+
+
+namespace fff
+{
+class AsyncCallback //actor pattern
+{
+public:
+ AsyncCallback() {}
+
+ //non-blocking: context of worker thread (and main thread, see reportStats())
+ void updateDataProcessed(int itemsDelta, int64_t bytesDelta) //noexcept!!
+ {
+ itemsDeltaProcessed_ += itemsDelta;
+ bytesDeltaProcessed_ += bytesDelta;
+ }
+ void updateDataTotal(int itemsDelta, int64_t bytesDelta) //noexcept!!
+ {
+ itemsDeltaTotal_ += itemsDelta;
+ bytesDeltaTotal_ += bytesDelta;
+ }
+
+ //context of worker thread
+ void reportStatus(const std::wstring& msg) //throw ThreadInterruption
+ {
+ assert(!zen::runningMainThread());
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ if (ThreadStatus* ts = getThreadStatus()) //call while holding "lockCurrentStatus_" lock!!
+ ts->statusMsg = msg;
+ }
+ zen::interruptionPoint(); //throw ThreadInterruption
+ }
+
+ //blocking call: context of worker thread
+ //=> indirect support for "pause": reportInfo() is called under singleThread lock,
+ // so all other worker threads will wait when coming out of parallel I/O (trying to lock singleThread)
+ void reportInfo(const std::wstring& msg) //throw ThreadInterruption
+ {
+ reportStatus(msg); //throw ThreadInterruption
+ logInfo (msg); //
+ }
+
+ //blocking call: context of worker thread
+ void logInfo(const std::wstring& msg) //throw ThreadInterruption
+ {
+ assert(!zen::runningMainThread());
+ std::unique_lock<std::mutex> dummy(lockRequest_);
+ zen::interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !logInfoRequest_; }); //throw ThreadInterruption
+
+ logInfoRequest_ = /*std::move(taskPrefix) + */ msg;
+
+ dummy.unlock(); //optimization for condition_variable::notify_all()
+ conditionNewRequest.notify_all();
+ }
+
+ //blocking call: context of worker thread
+ ProcessCallback::Response reportError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption
+ {
+ assert(!zen::runningMainThread());
+ std::unique_lock<std::mutex> dummy(lockRequest_);
+ zen::interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !errorRequest_ && !errorResponse_; }); //throw ThreadInterruption
+
+ errorRequest_ = ErrorInfo({ /*std::move(taskPrefix) + */ msg, retryNumber });
+ conditionNewRequest.notify_all();
+
+ zen::interruptibleWait(conditionHaveResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
+
+ ProcessCallback::Response rv = *errorResponse_;
+
+ errorRequest_ = zen::NoValue();
+ errorResponse_ = zen::NoValue();
+
+ dummy.unlock(); //optimization for condition_variable::notify_all()
+ conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::logInfo()
+ return rv;
+ }
+
+ //context of main thread
+ void waitUntilDone(std::chrono::milliseconds duration, ProcessCallback& cb) //throw X
+ {
+ assert(zen::runningMainThread());
+ for (;;)
+ {
+ const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
+
+ for (std::unique_lock<std::mutex> dummy(lockRequest_) ;;) //process all errors without delay
+ {
+ const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || logInfoRequest_ || finishNowRequest_; });
+ if (!rv) //time-out + condition not met
+ break;
+
+ if (errorRequest_ && !errorResponse_)
+ {
+ assert(!finishNowRequest_);
+ errorResponse_ = cb.reportError(errorRequest_->msg, errorRequest_->retryNumber); //throw X
+ conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ }
+ if (logInfoRequest_)
+ {
+ cb.logInfo(*logInfoRequest_);
+ logInfoRequest_ = zen::NoValue();
+ conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportError()
+ }
+ if (finishNowRequest_)
+ {
+ dummy.unlock(); //call member functions outside of mutex scope:
+ reportStats(cb); //one last call for accurate stat-reporting!
+ return;
+ }
+ }
+
+ //call member functions outside of mutex scope:
+ cb.reportStatus(getCurrentStatus()); //throw X
+ reportStats(cb);
+ }
+ }
+
+ void notifyTaskBegin(size_t prio) //noexcept
+ {
+ assert(!zen::runningMainThread());
+ const uint64_t threadId = zen::getThreadId();
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ //const size_t taskIdx = [&]() -> size_t
+ //{
+ // auto it = std::find(usedIndexNums_.begin(), usedIndexNums_.end(), false);
+ // if (it != usedIndexNums_.end())
+ // {
+ // *it = true;
+ // return it - usedIndexNums_.begin();
+ // }
+
+ // usedIndexNums_.push_back(true);
+ // return usedIndexNums_.size() - 1;
+ //}();
+
+ if (statusByPriority_.size() < prio + 1)
+ statusByPriority_.resize(prio + 1);
+
+ statusByPriority_[prio].push_back({ threadId, /*taskIdx,*/ std::wstring() });
+ }
+
+ void notifyTaskEnd() //noexcept
+ {
+ assert(!zen::runningMainThread());
+ const uint64_t threadId = zen::getThreadId();
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ for (auto& sbp : statusByPriority_)
+ for (ThreadStatus& ts : sbp)
+ if (ts.threadId == threadId)
+ {
+ //usedIndexNums_[ts.taskIdx] = false;
+ std::swap(ts, sbp.back());
+ sbp.pop_back();
+ return;
+ }
+ assert(false);
+ }
+
+ void notifyAllDone() //noexcept
+ {
+ std::lock_guard<std::mutex> dummy(lockRequest_);
+ assert(!finishNowRequest_);
+ finishNowRequest_ = true;
+ conditionNewRequest.notify_all(); //perf: should unlock mutex before notify!? (insignificant)
+ }
+
+private:
+ AsyncCallback (const AsyncCallback&) = delete;
+ AsyncCallback& operator=(const AsyncCallback&) = delete;
+
+ struct ThreadStatus
+ {
+ uint64_t threadId = 0;
+ //size_t taskIdx = 0; //nice human-readable task id for GUI
+ std::wstring statusMsg;
+ };
+
+ ThreadStatus* getThreadStatus() //call while holding "lockCurrentStatus_" lock!!
+ {
+ assert(!zen::runningMainThread());
+ const uint64_t threadId = zen::getThreadId();
+
+ for (auto& sbp : statusByPriority_)
+ for (ThreadStatus& ts : sbp)
+ if (ts.threadId == threadId)
+ return &ts;
+ assert(false);
+ return nullptr;
+ }
+
+#if 0 //maybe not that relevant after all!?
+ std::wstring getTaskPrefix() //call *outside* of "lockCurrentStatus_" lock!!
+ {
+ const size_t taskIdx = [&]
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ const ThreadStatus* ts = getThreadStatus(); //call while holding "lockCurrentStatus_" lock!!
+ return ts ? ts->taskIdx : static_cast<size_t>(-2);
+ }();
+ return totalThreadCount_ > 1 ? L"[" + zen::numberTo<std::wstring>(taskIdx + 1) + L"] " : L"";
+ }
+#endif
+
+ //context of main thread
+ void reportStats(ProcessCallback& cb)
+ {
+ assert(zen::runningMainThread());
+
+ const std::pair<int, int64_t> deltaProcessed(itemsDeltaProcessed_, bytesDeltaProcessed_);
+ if (deltaProcessed.first != 0 || deltaProcessed.second != 0)
+ {
+ updateDataProcessed (-deltaProcessed.first, -deltaProcessed.second); //careful with these atomics: don't just set to 0
+ cb.updateDataProcessed( deltaProcessed.first, deltaProcessed.second); //noexcept!!
+ }
+ const std::pair<int, int64_t> deltaTotal(itemsDeltaTotal_, bytesDeltaTotal_);
+ if (deltaTotal.first != 0 || deltaTotal.second != 0)
+ {
+ updateDataTotal (-deltaTotal.first, -deltaTotal.second);
+ cb.updateDataTotal( deltaTotal.first, deltaTotal.second); //noexcept!!
+ }
+ }
+
+ //context of main thread, call repreatedly
+ std::wstring getCurrentStatus()
+ {
+ assert(zen::runningMainThread());
+
+ int parallelOpsTotal = 0;
+ std::wstring statusMsg;
+ {
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+
+ for (const auto& sbp : statusByPriority_)
+ parallelOpsTotal += sbp.size();
+
+ statusMsg = [&]
+ {
+ for (const auto& sbp : statusByPriority_)
+ for (const ThreadStatus& ts : sbp)
+ if (!ts.statusMsg.empty())
+ return ts.statusMsg;
+ return std::wstring();
+ }();
+ }
+ if (parallelOpsTotal >= 2)
+ return L"[" + _P("1 thread", "%x threads", parallelOpsTotal) + L"] " + statusMsg;
+ else
+ return statusMsg;
+ }
+
+ struct ErrorInfo
+ {
+ std::wstring msg;
+ size_t retryNumber = 0;
+ };
+
+ //---- main <-> worker communication channel ----
+ std::mutex lockRequest_;
+ std::condition_variable conditionReadyForNewRequest_;
+ std::condition_variable conditionNewRequest;
+ std::condition_variable conditionHaveResponse_;
+ zen::Opt<ErrorInfo> errorRequest_;
+ zen::Opt<ProcessCallback::Response> errorResponse_;
+ zen::Opt<std::wstring> logInfoRequest_;
+ bool finishNowRequest_ = false;
+
+ //---- status updates ----
+ std::mutex lockCurrentStatus_; //different lock for status updates so that we're not blocked by other threads reporting errors
+ std::vector<std::vector<ThreadStatus>> statusByPriority_;
+ //give status messages priority according to their folder pair (e.g. first folder pair has prio 0) => visualize (somewhat) natural processing order
+
+ //std::vector<char/*bool*/> usedIndexNums_; //keep info for human-readable task index numbers
+
+ //---- status updates II (lock-free) ----
+ std::atomic<int> itemsDeltaProcessed_{ 0 }; //
+ std::atomic<int64_t> bytesDeltaProcessed_{ 0 }; //std:atomic is uninitialized by default!
+ std::atomic<int> itemsDeltaTotal_ { 0 }; //
+ std::atomic<int64_t> bytesDeltaTotal_ { 0 }; //
+};
+
+
+//manage statistics reporting for a single item of work
+template <class Callback = ProcessCallback>
+class ItemStatReporter
+{
+public:
+ ItemStatReporter(int itemsExpected, int64_t bytesExpected, Callback& cb) :
+ itemsExpected_(itemsExpected),
+ bytesExpected_(bytesExpected),
+ cb_(cb) {}
+
+ ~ItemStatReporter()
+ {
+ const bool scopeFail = std::uncaught_exceptions() > exeptionCount_;
+ if (scopeFail)
+ cb_.updateDataTotal(itemsReported_, bytesReported_); //=> unexpected increase of total workload
+ else
+ //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams,
+ //less for sparse and compressed files, or file changed in the meantime!
+ cb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
+ }
+
+ void reportStatus(const std::wstring& msg) { cb_.reportStatus(msg); } //throw ThreadInterruption
+
+ void reportDelta(int itemsDelta, int64_t bytesDelta) //nothrow!
+ {
+ cb_.updateDataProcessed(itemsDelta, bytesDelta); //nothrow!
+ itemsReported_ += itemsDelta;
+ bytesReported_ += bytesDelta;
+
+ //special rule: avoid temporary statistics mess up, even though they are corrected anyway below:
+ if (itemsReported_ > itemsExpected_)
+ {
+ cb_.updateDataTotal(itemsReported_ - itemsExpected_, 0);
+ itemsReported_ = itemsExpected_;
+ }
+ if (bytesReported_ > bytesExpected_)
+ {
+ cb_.updateDataTotal(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
+ bytesReported_ = bytesExpected_;
+ }
+ }
+
+private:
+ int itemsReported_ = 0;
+ int64_t bytesReported_ = 0;
+ const int itemsExpected_;
+ const int64_t bytesExpected_;
+ Callback& cb_;
+ const int exeptionCount_ = std::uncaught_exceptions();
+};
+
+using AsyncItemStatReporter = ItemStatReporter<AsyncCallback>;
+
+//=====================================================================================================================
+
+template <class Function, class Callback> inline //return ignored error message if available
+std::wstring tryReportingError(Function cmd /*throw FileError*/, Callback& cb /*throw X*/)
+{
+ for (size_t retryNumber = 0;; ++retryNumber)
+ try
+ {
+ cmd(); //throw FileError
+ return std::wstring();
+ }
+ catch (zen::FileError& e)
+ {
+ assert(!e.toString().empty());
+ switch (cb.reportError(e.toString(), retryNumber)) //throw X
+ {
+ case ProcessCallback::IGNORE_ERROR:
+ return e.toString();
+ case ProcessCallback::RETRY:
+ break; //continue with loop
+ }
+ }
+}
+
+//=====================================================================================================================
+
+template <class Function> inline
+auto parallelScope(Function&& fun, std::mutex& singleThread) //throw X
+{
+ singleThread.unlock();
+ ZEN_ON_SCOPE_EXIT(singleThread.lock());
+
+ return fun(); //throw X
+}
+}
+
+#endif //STATUS_HANDLER_IMPL_H_07682758976
diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/base/structures.cpp
index a882807f..eff069ce 100755
--- a/FreeFileSync/Source/structures.cpp
+++ b/FreeFileSync/Source/base/structures.cpp
@@ -10,7 +10,7 @@
#include <ctime>
#include <zen/i18n.h>
#include <zen/time.h>
-#include "lib/hard_filter.h"
+#include "hard_filter.h"
using namespace zen;
using namespace fff;
@@ -110,7 +110,7 @@ std::wstring fff::getVariantName(DirectionConfig::Variant var)
}
-//use in sync log files where users expect ANSI: https://www.freefilesync.org/forum/viewtopic.php?t=4647
+//use in sync log files where users expect ANSI: https://freefilesync.org/forum/viewtopic.php?t=4647
std::wstring fff::getVariantNameForLog(DirectionConfig::Variant var)
{
return getVariantNameImpl(var, L"<-", L"->", L">");
diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/base/structures.h
index 2403cf8d..90e565b6 100755
--- a/FreeFileSync/Source/structures.h
+++ b/FreeFileSync/Source/base/structures.h
@@ -11,7 +11,7 @@
#include <memory>
#include <zen/zstring.h>
#include <zen/optional.h>
-#include "fs/abstract.h"
+#include "../fs/abstract.h"
namespace fff
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp
index 89c9684c..273ffb9b 100755
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/base/synchronization.cpp
@@ -11,13 +11,13 @@
#include <zen/guid.h>
#include <zen/crc.h>
#include "algorithm.h"
-#include "lib/db_file.h"
-#include "lib/dir_exist_async.h"
-#include "lib/status_handler_impl.h"
-#include "lib/versioning.h"
-#include "lib/binary.h"
-#include "fs/concrete.h"
-#include "fs/native.h"
+#include "db_file.h"
+#include "dir_exist_async.h"
+#include "status_handler_impl.h"
+#include "versioning.h"
+#include "binary.h"
+#include "../fs/concrete.h"
+#include "../fs/native.h"
#include <unistd.h> //fsync
#include <fcntl.h> //open
@@ -368,16 +368,6 @@ void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath,
namespace parallel
{
-template <class Function> inline
-auto parallelScope(Function fun, std::mutex& singleThread) //throw X
-{
- singleThread.unlock();
- ZEN_ON_SCOPE_EXIT(singleThread.lock());
-
- return fun(); //throw X
-}
-
-
inline
AFS::ItemType getItemType(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
{ return parallelScope([ap] { return AFS::getItemType(ap); /*throw FileError*/ }, singleThread); }
@@ -467,306 +457,6 @@ void verifyFiles(const AbstractPath& apSource, const AbstractPath& apTarget, con
}
-
-namespace
-{
-class AsyncCallback //actor pattern
-{
-public:
- AsyncCallback(size_t threadCount) : threadStatus_(threadCount), totalThreadCount_(threadCount) {}
-
- //non-blocking: context of worker thread
- void updateDataProcessed(int itemsDelta, int64_t bytesDelta) //noexcept!!
- {
- itemsDeltaProcessed_ += itemsDelta;
- bytesDeltaProcessed_ += bytesDelta;
- }
- void updateDataTotal(int itemsDelta, int64_t bytesDelta) //noexcept!!
- {
- itemsDeltaTotal_ += itemsDelta;
- bytesDeltaTotal_ += bytesDelta;
- }
-
- //context of main thread
- void reportStats(ProcessCallback& cb)
- {
- assert(std::this_thread::get_id() == mainThreadId);
-
- const std::pair<int, int64_t> deltaProcessed(itemsDeltaProcessed_, bytesDeltaProcessed_);
- if (deltaProcessed.first != 0 || deltaProcessed.second != 0)
- {
- updateDataProcessed (-deltaProcessed.first, -deltaProcessed.second); //careful with these atomics: don't just set to 0
- cb.updateDataProcessed( deltaProcessed.first, deltaProcessed.second); //noexcept!!
- }
- const std::pair<int, int64_t> deltaTotal(itemsDeltaTotal_, bytesDeltaTotal_);
- if (deltaTotal.first != 0 || deltaTotal.second != 0)
- {
- updateDataTotal (-deltaTotal.first, -deltaTotal.second);
- cb.updateDataTotal( deltaTotal.first, deltaTotal.second); //noexcept!!
- }
- }
-
- //context of worker thread
- void reportStatus(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
- {
- assert(std::this_thread::get_id() != mainThreadId);
- std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
-
- assert(threadStatus_[threadIdx].active);
- threadStatus_[threadIdx].statusMsg = msg;
-
- interruptionPoint(); //throw ThreadInterruption
- }
-
- //context of main thread, call repreatedly
- std::wstring getCurrentStatus()
- {
- assert(std::this_thread::get_id() == mainThreadId);
-
- int activeThreadCount = 0;
- std::wstring statusMsg;
- {
- std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
-
- for (const ThreadStatus& ts : threadStatus_)
- if (ts.active)
- {
- ++activeThreadCount;
- if (statusMsg.empty())
- statusMsg = ts.statusMsg;
- }
- }
-
- std::wstring output;
- if (activeThreadCount >= 2)
- output = L"[" + _P("1 thread", "%x threads", activeThreadCount) + L"] ";
- output += statusMsg;
- return output;
- }
-
- //blocking call: context of worker thread
- //=> indirect support for "pause": reportInfo() is called under singleThread lock,
- // so all other worker threads will wait when coming out of parallel I/O (trying to lock singleThread)
- void reportInfo(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
- {
- reportStatus(msg, threadIdx); //throw ThreadInterruption
- logInfo (msg, threadIdx); //
- }
-
- //blocking call: context of worker thread
- void logInfo(const std::wstring& msg, size_t threadIdx) //throw ThreadInterruption
- {
- assert(std::this_thread::get_id() != mainThreadId);
- std::unique_lock<std::mutex> dummy(lockRequest_);
- interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !logInfoRequest_; }); //throw ThreadInterruption
-
- logInfoRequest_ = (totalThreadCount_ > 1 ? L"[" + numberTo<std::wstring>(threadIdx + 1) + L"] " : L"") + msg;
-
- dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionNewRequest.notify_all();
- }
-
- //blocking call: context of worker thread
- ProcessCallback::Response reportError(const std::wstring& msg, size_t retryNumber, size_t threadIdx) //throw ThreadInterruption
- {
- assert(std::this_thread::get_id() != mainThreadId);
- std::unique_lock<std::mutex> dummy(lockRequest_);
- interruptibleWait(conditionReadyForNewRequest_, dummy, [this] { return !errorRequest_ && !errorResponse_; }); //throw ThreadInterruption
-
- errorRequest_ = ErrorInfo({ (totalThreadCount_ > 1 ? L"[" + numberTo<std::wstring>(threadIdx + 1) + L"] " : L"") + msg, retryNumber });
- conditionNewRequest.notify_all();
-
- interruptibleWait(conditionHaveResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
-
- ProcessCallback::Response rv = *errorResponse_;
-
- errorRequest_ = NoValue();
- errorResponse_ = NoValue();
-
- dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::logInfo()
- return rv;
- }
-
- //context of main thread
- void waitUntilDone(std::chrono::milliseconds duration, ProcessCallback& cb) //throw X
- {
- assert(std::this_thread::get_id() == mainThreadId);
- for (;;)
- {
- const std::chrono::steady_clock::time_point callbackTime = std::chrono::steady_clock::now() + duration;
-
- for (std::unique_lock<std::mutex> dummy(lockRequest_) ;;) //process all errors without delay
- {
- const bool rv = conditionNewRequest.wait_until(dummy, callbackTime, [this] { return (errorRequest_ && !errorResponse_) || logInfoRequest_ || finishNowRequest_; });
- if (!rv) //time-out + condition not met
- break;
-
- if (errorRequest_ && !errorResponse_)
- {
- assert(!finishNowRequest_);
- errorResponse_ = cb.reportError(errorRequest_->msg, errorRequest_->retryNumber); //throw X
- conditionHaveResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
- }
- if (logInfoRequest_)
- {
- cb.logInfo(*logInfoRequest_);
- logInfoRequest_ = NoValue();
- conditionReadyForNewRequest_.notify_all(); //=> spurious wake-up for AsyncCallback::reportError()
- }
- if (finishNowRequest_)
- {
- dummy.unlock(); //call member functions outside of mutex scope:
- reportStats(cb); //one last call for accurate stat-reporting!
- return;
- }
- }
-
- //call member functions outside of mutex scope:
- cb.reportStatus(getCurrentStatus()); //throw X
- reportStats(cb);
- }
- }
-
- void notifyWorkBegin(size_t threadIdx) //noexcept
- {
- std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
- assert(!threadStatus_[threadIdx].active);
- threadStatus_[threadIdx].active = true;
- }
-
- void notifyWorkEnd(size_t threadIdx) //noexcept
- {
- std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
- assert(threadStatus_[threadIdx].active);
- threadStatus_[threadIdx].active = false;
- threadStatus_[threadIdx].statusMsg.clear();
- }
-
- void notifyAllDone() //noexcept
- {
- std::lock_guard<std::mutex> dummy(lockRequest_);
- assert(!finishNowRequest_);
- finishNowRequest_ = true;
- conditionNewRequest.notify_all(); //perf: should unlock mutex before notify!? (insignificant)
- }
-
-private:
- AsyncCallback (const AsyncCallback&) = delete;
- AsyncCallback& operator=(const AsyncCallback&) = delete;
-
- struct ThreadStatus
- {
- bool active = false;
- std::wstring statusMsg;
- };
- struct ErrorInfo
- {
- std::wstring msg;
- size_t retryNumber = 0;
- };
-
- //---- main <-> worker communication channel ----
- std::mutex lockRequest_;
- std::condition_variable conditionReadyForNewRequest_;
- std::condition_variable conditionNewRequest;
- std::condition_variable conditionHaveResponse_;
- Opt<ErrorInfo> errorRequest_;
- Opt<ProcessCallback::Response> errorResponse_;
- Opt<std::wstring> logInfoRequest_;
- bool finishNowRequest_ = false;
-
- //---- status updates ----
- std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error
- std::vector<ThreadStatus> threadStatus_;
-
- const size_t totalThreadCount_;
-
- //---- status updates II (lock-free) ----
- std::atomic<int> itemsDeltaProcessed_{ 0 }; //
- std::atomic<int64_t> bytesDeltaProcessed_{ 0 }; //std:atomic is uninitialized by default!
- std::atomic<int> itemsDeltaTotal_ { 0 }; //
- std::atomic<int64_t> bytesDeltaTotal_ { 0 }; //
-};
-
-
-template <typename Function> inline //return ignored error message if available
-Opt<std::wstring> tryReportingError(Function cmd, size_t threadIdx, AsyncCallback& acb) //throw ThreadInterruption
-{
- for (size_t retryNumber = 0;; ++retryNumber)
- try
- {
- cmd(); //throw FileError
- return NoValue();
- }
- catch (FileError& error)
- {
- switch (acb.reportError(error.toString(), retryNumber, threadIdx)) //throw ThreadInterruption
- {
- case ProcessCallback::IGNORE_ERROR:
- return error.toString();
- case ProcessCallback::RETRY:
- break; //continue with loop
- }
- }
-}
-
-
-//manage statistics reporting for a single item of work
-class AsyncItemStatReporter
-{
-public:
- AsyncItemStatReporter(int itemsExpected, int64_t bytesExpected, size_t threadIdx, AsyncCallback& acb) :
- itemsExpected_(itemsExpected),
- bytesExpected_(bytesExpected),
- threadIdx_(threadIdx),
- acb_(acb) {}
-
- ~AsyncItemStatReporter()
- {
- const bool scopeFail = getUncaughtExceptionCount() > exeptionCount_;
- if (scopeFail)
- acb_.updateDataTotal(itemsReported_, bytesReported_); //=> unexpected increase of total workload
- else
- //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams,
- //less for sparse and compressed files, or file changed in the meantime!
- acb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
- }
-
- void reportStatus(const std::wstring& text) { acb_.reportStatus(text, threadIdx_); } //throw ThreadInterruption
-
- void reportDelta(int itemsDelta, int64_t bytesDelta) //throw ThreadInterruption
- {
- acb_.updateDataProcessed(itemsDelta, bytesDelta); //nothrow!
- itemsReported_ += itemsDelta;
- bytesReported_ += bytesDelta;
-
- //special rule: avoid temporary statistics mess up, even though they are corrected anyway below:
- if (itemsReported_ > itemsExpected_)
- {
- acb_.updateDataTotal(itemsReported_ - itemsExpected_, 0);
- itemsReported_ = itemsExpected_;
- }
- if (bytesReported_ > bytesExpected_)
- {
- acb_.updateDataTotal(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
- bytesReported_ = bytesExpected_;
- }
-
- interruptionPoint(); //throw ThreadInterruption
- }
-
-private:
- int itemsReported_ = 0;
- int64_t bytesReported_ = 0;
- const int itemsExpected_;
- const int64_t bytesExpected_;
- const size_t threadIdx_;
- AsyncCallback& acb_;
- const int exeptionCount_ = getUncaughtExceptionCount();
-};
-}
-
//#################################################################################################################
//#################################################################################################################
@@ -883,7 +573,7 @@ txtRemovingFolder_([&]
void DeletionHandling::tryCleanup(ProcessCallback& cb /*throw X*/, bool allowCallbackException) //throw FileError
{
- assert(std::this_thread::get_id() == mainThreadId);
+ assert(runningMainThread());
switch (deletionPolicy_)
{
case DeletionPolicy::PERMANENT:
@@ -941,9 +631,10 @@ void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath,//th
auto notifyDeletion = [&statReporter](const std::wstring& statusText, const std::wstring& displayPath)
{
statReporter.reportStatus(replaceCpy(statusText, L"%x", fmtPath(displayPath))); //throw ThreadInterruption
- statReporter.reportDelta(1, 0); //throw ThreadInterruption; it would be more correct to report *after* work was done!
+ statReporter.reportDelta(1, 0); //it would be more correct to report *after* work was done!
+ //OTOH: ThreadInterruption must not happen after last deletion was successful: allow for transactional file model update!
};
- static_assert(std::is_const<decltype(txtRemovingFile_)>::value, "callbacks better be thread-safe!");
+ static_assert(std::is_const_v<decltype(txtRemovingFile_)>, "callbacks better be thread-safe!");
auto onBeforeFileDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFile_, displayPath); };
auto onBeforeDirDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFolder_, displayPath); };
@@ -953,7 +644,7 @@ void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath,//th
case DeletionPolicy::RECYCLER:
parallel::recycleItem(getOrCreateRecyclerSession(), folderPath, relativePath, singleThread); //throw FileError
- statReporter.reportDelta(1, 0); //throw ThreadInterruption; moving to recycler is ONE logical operation, irrespective of the number of child elements!
+ statReporter.reportDelta(1, 0); //moving to recycler is ONE logical operation, irrespective of the number of child elements!
break;
case DeletionPolicy::VERSIONING:
@@ -962,12 +653,12 @@ void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath,//th
auto notifyMove = [&statReporter](const std::wstring& statusText, const std::wstring& displayPathFrom, const std::wstring& displayPathTo)
{
statReporter.reportStatus(replaceCpy(replaceCpy(statusText, L"%x", L"\n" + fmtPath(displayPathFrom)), L"%y", L"\n" + fmtPath(displayPathTo))); //throw ThreadInterruption
- statReporter.reportDelta(1, 0); //throw ThreadInterruption; it would be more correct to report *after* work was done!
+ statReporter.reportDelta(1, 0); //it would be more correct to report *after* work was done!
};
- static_assert(std::is_const<decltype(txtMovingFileXtoY_)>::value, "callbacks better be thread-safe!");
+ static_assert(std::is_const_v<decltype(txtMovingFileXtoY_)>, "callbacks better be thread-safe!");
auto onBeforeFileMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFileXtoY_, displayPathFrom, displayPathTo); };
auto onBeforeFolderMove = [&](const std::wstring& displayPathFrom, const std::wstring& displayPathTo) { notifyMove(txtMovingFolderXtoY_, displayPathFrom, displayPathTo); };
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; //throw ThreadInterruption
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); interruptionPoint(); }; //throw ThreadInterruption
parallel::revisionFolder(getOrCreateVersioner(), folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, notifyUnbufferedIO, singleThread); //throw FileError, ThreadInterruption
}
@@ -995,7 +686,7 @@ void DeletionHandling::removeFileWithCallback(const FileDescriptor& fileDescr, /
case DeletionPolicy::VERSIONING:
{
//callback runs *outside* singleThread_ lock! => fine
- auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; //throw ThreadInterruption
+ auto notifyUnbufferedIO = [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); interruptionPoint(); }; //throw ThreadInterruption
parallel::revisionFile(getOrCreateVersioner(), fileDescr, relativePath, notifyUnbufferedIO, singleThread); //throw FileError
}
@@ -1004,7 +695,7 @@ void DeletionHandling::removeFileWithCallback(const FileDescriptor& fileDescr, /
//even if the source item does not exist anymore, significant I/O work was done => report
//-> also consider unconditional statReporter.reportDelta(-1, 0) when overwriting a file
- statReporter.reportDelta(1, 0); //throw ThreadInterruption
+ statReporter.reportDelta(1, 0);
}
@@ -1024,9 +715,10 @@ void DeletionHandling::removeLinkWithCallback(const AbstractPath& linkPath, //th
parallel::revisionSymlink(getOrCreateVersioner(), linkPath, relativePath, singleThread); //throw FileError
break;
}
+ //remain transactional as much as possible => no more callbacks that can throw after successful deletion! (next: update file model!)
//report unconditionally, see removeFileWithCallback()
- statReporter.reportDelta(1, 0); //throw ThreadInterruption
+ statReporter.reportDelta(1, 0);
}
//------------------------------------------------------------------------------------------------------------
@@ -1113,7 +805,9 @@ private:
int64_t spaceNeededRight_ = 0;
};
-//----------------------------------------------------------------------------------------
+//===================================================================================================
+//===================================================================================================
+
class Workload;
class FolderPairSyncer
@@ -1147,16 +841,14 @@ private:
PASS_NEVER //skip item
};
- FolderPairSyncer(SyncCtx& syncCtx, Workload& workload, std::mutex& singleThread, size_t threadIdx, AsyncCallback& acb) :
+ FolderPairSyncer(SyncCtx& syncCtx, std::mutex& singleThread, AsyncCallback& acb) :
errorsModTime_ (syncCtx.errorsModTime),
delHandlingLeft_ (syncCtx.delHandlingLeft),
delHandlingRight_ (syncCtx.delHandlingRight),
verifyCopiedFiles_ (syncCtx.verifyCopiedFiles),
copyFilePermissions_(syncCtx.copyFilePermissions),
failSafeFileCopy_ (syncCtx.failSafeFileCopy),
- workload_(workload),
singleThread_(singleThread),
- threadIdx_(threadIdx),
acb_(acb) {}
static PassNo getPass(const FilePair& file);
@@ -1165,9 +857,10 @@ private:
static void runPass(PassNo pass, SyncCtx& syncCtx, BaseFolderPair& baseFolder, ProcessCallback& cb); //throw X
- static void appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, //in
- std::vector<std::function<void(FolderPairSyncer& fps)>>& workItems, //out
- std::vector<ContainerObject*>& foldersToProcess); //
+ void appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, //in
+ Workload& workload,
+ RingBuffer<std::function<void()>>& workItems, //out
+ RingBuffer<ContainerObject*>& foldersToProcess); //
template <SelectedSide side>
void setup2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError, ThreadInterruption
@@ -1185,10 +878,10 @@ private:
void synchronizeFolder(FolderPair& folder); //
template <SelectedSide sideTrg> void synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp); //throw FileError, ThreadInterruption
- void reportInfo(const std::wstring& rawText, const std::wstring& displayPath) { acb_.reportInfo(replaceCpy(rawText, L"%x", fmtPath(displayPath)), threadIdx_); }
- void reportInfo(const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2)
+ void reportInfo(const std::wstring& rawText, const std::wstring& displayPath) { acb_.reportInfo(replaceCpy(rawText, L"%x", fmtPath(displayPath))); }
+ void reportInfo(const std::wstring& rawText, const std::wstring& displayPath1, const std::wstring& displayPath2) //throw ThreadInterruption
{
- acb_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtPath(displayPath1)), L"%y", L"\n" + fmtPath(displayPath2)), threadIdx_);
+ acb_.reportInfo(replaceCpy(replaceCpy(rawText, L"%x", L"\n" + fmtPath(displayPath1)), L"%y", L"\n" + fmtPath(displayPath2))); //throw ThreadInterruption
}
//target existing after onDeleteTargetFile(): undefined behavior! (fail/overwrite/auto-rename)
@@ -1205,9 +898,7 @@ private:
const bool copyFilePermissions_;
const bool failSafeFileCopy_;
- Workload& workload_;
std::mutex& singleThread_;
- const size_t threadIdx_;
AsyncCallback& acb_;
//preload status texts (premature?)
@@ -1222,7 +913,8 @@ private:
const std::wstring txtSourceItemNotFound_{_("Source item %x not found" )};
};
-//---------------------------------------------------------------------------------------------------------------
+//===================================================================================================
+//===================================================================================================
/* ___________________________
| |
| Multithreaded File Copy |
@@ -1245,15 +937,18 @@ Notes: - All threads share a single mutex, unlocked only during file I/O => do N
- Maximize opportunity for parallelization ASAP: Workload buckets serve folder-items *before* files/symlinks => reduce risk of work-stealing
- Memory consumption: "Folders to process" may grow indefinitely; however: test case "C:\", 100.000 folders => worst case only ~ 800kB on x64
*/
-
class Workload
{
public:
- Workload(FolderPairSyncer::PassNo pass, BaseFolderPair& baseFolder, size_t threadCount, AsyncCallback& acb) :
- pass_(pass), acb_(acb), workload_(threadCount), foldersToProcess_{ &baseFolder } { assert(threadCount > 0); }
+ Workload(FolderPairSyncer& fps, FolderPairSyncer::PassNo pass, BaseFolderPair& baseFolder, size_t threadCount, AsyncCallback& acb) :
+ fps_(fps), pass_(pass), acb_(acb), workload_(threadCount)
+ {
+ foldersToProcess_.push_back(&baseFolder);
+ assert(threadCount > 0);
+ }
//blocking call: context of worker thread
- std::function<void(FolderPairSyncer& fps)> getNext(size_t threadIdx) //throw ThreadInterruption
+ std::function<void()> getNext(size_t threadIdx) //throw ThreadInterruption
{
std::unique_lock<std::mutex> dummy(lockWork_);
for (;;)
@@ -1262,19 +957,19 @@ public:
{
if (!workload_[threadIdx].empty())
{
- auto workItem = workload_[threadIdx]. back(); //yes, no strong exception guarantee (std::bad_alloc)
- /**/ workload_[threadIdx].pop_back(); //
+ auto workItem = workload_[threadIdx]. front(); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workload_[threadIdx].pop_front(); //
return workItem;
}
if (!foldersToProcess_.empty())
{
- ContainerObject& hierObj = *foldersToProcess_. back();
- /**/ foldersToProcess_.pop_back();
+ ContainerObject& hierObj = *foldersToProcess_. front();
+ /**/ foldersToProcess_.pop_front();
//thread-safe thanks to std::mutex singleThread:
- FolderPairSyncer::appendFolderLevelWorkItems(pass_, hierObj, //in
- workload_[threadIdx], //out, appending
- foldersToProcess_); //
+ fps_.appendFolderLevelWorkItems(pass_, hierObj, *this, //in
+ workload_[threadIdx], //out, appending
+ foldersToProcess_); //
}
else
break;
@@ -1284,18 +979,19 @@ public:
WorkItems& items = *std::max_element(workload_.begin(), workload_.end(), [](const WorkItems& lhs, const WorkItems& rhs) { return lhs.size() < rhs.size(); });
if (!items.empty()) //=> != workload_[threadIdx]
{
- size_t pos = 0;
- erase_if(items, [&](const WorkItem& wi)
+ const size_t sz = items.size(); //[!] variable during loop!
+ for (size_t i = 0; i < sz; ++i)
{
- if (pos++ % 2 == 0)
- {
- workload_[threadIdx].push_back(wi);
- return true;
- }
- return false;
- });
- auto workItem = workload_[threadIdx]. back(); //yes, no strong exception guarantee (std::bad_alloc)
- /**/ workload_[threadIdx].pop_back(); //
+ auto wi = std::move(items. front());
+ /**/ items.pop_front();
+ if (i % 2 == 0)
+ workload_[threadIdx].push_back(std::move(wi));
+ else
+ items.push_back(std::move(wi));
+ }
+
+ auto workItem = workload_[threadIdx]. front(); //yes, no strong exception guarantee (std::bad_alloc)
+ /**/ workload_[threadIdx].pop_front(); //
return workItem;
}
@@ -1303,9 +999,6 @@ public:
acb_.notifyAllDone(); //noexcept
ZEN_ON_SCOPE_EXIT(--idleThreads_);
- acb_.notifyWorkEnd(threadIdx);
- ZEN_ON_SCOPE_EXIT(acb_.notifyWorkBegin(threadIdx));
-
auto haveNewWork = [&] { return !foldersToProcess_.empty() || std::any_of(workload_.begin(), workload_.end(), [](const WorkItems& wi) { return !wi.empty(); }); };
interruptibleWait(conditionNewWork_, dummy, [&] { return haveNewWork(); }); //throw ThreadInterruption
@@ -1326,9 +1019,10 @@ private:
Workload (const Workload&) = delete;
Workload& operator=(const Workload&) = delete;
- using WorkItem = std::function<void(FolderPairSyncer& fps) /*throw ThreadInterruption*/>;
- using WorkItems = std::vector<WorkItem>;
+ using WorkItem = std::function<void() /*throw ThreadInterruption*/>;
+ using WorkItems = RingBuffer<WorkItem>; //FIFO!
+ FolderPairSyncer& fps_;
const FolderPairSyncer::PassNo pass_;
AsyncCallback& acb_;
@@ -1338,7 +1032,7 @@ private:
size_t idleThreads_ = 0;
std::vector<WorkItems> workload_; //thread-specific buckets
- std::vector<ContainerObject*> foldersToProcess_;
+ RingBuffer<ContainerObject*> foldersToProcess_; //FIFO!
};
@@ -1348,26 +1042,26 @@ void FolderPairSyncer::runPass(PassNo pass, SyncCtx& syncCtx, BaseFolderPair& ba
std::mutex singleThread; //only a single worker thread may run at a time, except for parallel file I/O
- AsyncCallback acb(threadCount); //
- Workload workload(pass, baseFolder, threadCount, acb); //manage life time: enclose InterruptibleThread's!!!
-
- FixedList<InterruptibleThread> worker;
+ AsyncCallback acb; //
+ FolderPairSyncer fps(syncCtx, singleThread, acb); //manage life time: enclose InterruptibleThread's!!!
+ Workload workload(fps, pass, baseFolder, threadCount, acb); //
+ std::vector<InterruptibleThread> worker;
ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.join(); );
ZEN_ON_SCOPE_EXIT( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join
for (size_t threadIdx = 0; threadIdx < threadCount; ++threadIdx)
- worker.emplace_back([fps = FolderPairSyncer(syncCtx, workload, singleThread, threadIdx, acb), threadIdx, &singleThread, &acb, &workload]() mutable
+ worker.emplace_back([threadIdx, &singleThread, &acb, &workload]
{
setCurrentThreadName(("Sync Worker[" + numberTo<std::string>(threadIdx) + "]").c_str());
- acb.notifyWorkBegin(threadIdx);
- ZEN_ON_SCOPE_EXIT(acb.notifyWorkEnd(threadIdx));
-
- while (/*blocking call:*/ std::function<void(FolderPairSyncer& fps)> workItem = workload.getNext(threadIdx)) //throw ThreadInterruption
+ while (/*blocking call:*/ std::function<void()> workItem = workload.getNext(threadIdx)) //throw ThreadInterruption
{
+ acb.notifyTaskBegin(0 /*prio*/); //same prio, while processing only one folder pair at a time
+ ZEN_ON_SCOPE_EXIT(acb.notifyTaskEnd());
+
std::lock_guard<std::mutex> dummy(singleThread); //protect ALL accesses to "fps" and workItem execution!
- workItem(fps); //throw ThreadInterruption
+ workItem(); //throw ThreadInterruption
}
});
@@ -1375,21 +1069,18 @@ void FolderPairSyncer::runPass(PassNo pass, SyncCtx& syncCtx, BaseFolderPair& ba
}
-void FolderPairSyncer::appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, //in
- std::vector<std::function<void(FolderPairSyncer& fps)>>& workItems, //out
- std::vector<ContainerObject* >& foldersToProcess) //
+void FolderPairSyncer::appendFolderLevelWorkItems(PassNo pass, ContainerObject& hierObj, Workload& workload, //in
+ RingBuffer<std::function<void()>>& workItems, //out
+ RingBuffer<ContainerObject* >& foldersToProcess) //
{
- const size_t itemCountOld = workItems.size();
- const size_t folderCountOld = foldersToProcess.size();
-
//synchronize folders:
for (FolderPair& folder : hierObj.refSubFolders())
if (pass == getPass(folder))
- workItems.push_back([&folder](FolderPairSyncer& fps)
+ workItems.push_back([this, &folder, &workload]
{
- tryReportingError([&] { fps.synchronizeFolder(folder); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
- fps.workload_.addFolderToProcess(folder);
- warn_static("unnatural processing order!?")
+ tryReportingError([&] { synchronizeFolder(folder); }, acb_); //throw ThreadInterruption
+
+ workload.addFolderToProcess(folder);
});
else
foldersToProcess.push_back(&folder);
@@ -1397,24 +1088,20 @@ void FolderPairSyncer::appendFolderLevelWorkItems(PassNo pass, ContainerObject&
//synchronize files:
for (FilePair& file : hierObj.refSubFiles())
if (pass == PASS_ZERO)
- workItems.push_back([&file](FolderPairSyncer& fps) { fps.prepareFileMove(file); /*throw ThreadInterruption*/ });
- else if (pass == getPass(file))
- workItems.push_back([&file](FolderPairSyncer& fps)
- {
- tryReportingError([&] { fps.synchronizeFile(file); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
- });
+ workItems.push_back([this, &file] { prepareFileMove(file); /*throw ThreadInterruption*/ });
+ else if (pass == getPass(file))
+ workItems.push_back([this, &file]
+ {
+ tryReportingError([&] { synchronizeFile(file); }, acb_); //throw ThreadInterruption
+ });
//synchronize symbolic links:
for (SymlinkPair& symlink : hierObj.refSubLinks())
if (pass == getPass(symlink))
- workItems.push_back([&symlink](FolderPairSyncer& fps)
+ workItems.push_back([this, &symlink]
{
- tryReportingError([&] { fps.synchronizeLink(symlink); }, fps.threadIdx_, fps.acb_); //throw ThreadInterruption
+ tryReportingError([&] { synchronizeLink(symlink); }, acb_); //throw ThreadInterruption
});
-
- //ensure natural processing order despite LIFO:
- std::reverse(workItems .begin() + itemCountOld, workItems .end());
- std::reverse(foldersToProcess.begin() + folderCountOld, foldersToProcess.end());
}
@@ -1482,8 +1169,8 @@ void FolderPairSyncer::setup2StepMove(FilePair& sourceObj, //throw FileError, Th
//update file hierarchy
FilePair& tempFile = sourceObj.base().addSubFile<side>(afterLast(sourceRelPathTmp, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL), sourceObj.getAttributes<side>());
- static_assert(IsSameType<FixedList<FilePair>, ContainerObject::FileList>::value,
- "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because FixedList iterators are not invalidated by insertion!");
+ static_assert(std::is_same_v<ContainerObject::FileList, std::list<FilePair>>,
+ "ATTENTION: we're adding to the file list WHILE looping over it! This is only working because std::list iterators are not invalidated by insertion!");
sourceObj.removeObject<side>(); //remove only *after* evaluating "sourceObj, side"!
//note: this new item is *not* considered at the end of 0th pass because "!sourceWillBeDeleted && !haveNameClash"
@@ -1592,15 +1279,15 @@ void FolderPairSyncer::prepareFileMove(FilePair& file) //throw ThreadInterruptio
FilePair* sourceObj = &file;
assert(dynamic_cast<FilePair*>(FileSystemObject::retrieve(targetObj->getMoveRef())) == sourceObj);
- Opt<std::wstring> errMsg = tryReportingError([&] //throw ThreadInterruption
+ const std::wstring errMsg = tryReportingError([&] //throw ThreadInterruption
{
if (syncOp == SO_MOVE_LEFT_FROM)
resolveMoveConflicts<LEFT_SIDE>(*sourceObj, *targetObj); //throw FileError, ThreadInterruption
else
resolveMoveConflicts<RIGHT_SIDE>(*sourceObj, *targetObj); //
- }, threadIdx_, acb_); //throw ThreadInterruption
+ }, acb_); //throw ThreadInterruption
- if (errMsg)
+ if (!errMsg.empty())
{
//move operation has failed! We cannot allow to continue and have move source's parent directory deleted, messing up statistics!
// => revert to ordinary "copy + delete"
@@ -1767,7 +1454,7 @@ void FolderPairSyncer::synchronizeFile(FilePair& file) //throw FileError, Thread
template <SelectedSide sideTrg>
void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
@@ -1781,9 +1468,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
//can't use "getAbstractPath<sideTrg>()" as file name is not available!
const AbstractPath targetPath = file.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingFile_, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingFile_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
- AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), acb_);
try
{
const AFS::FileCopyResult result = copyFileWithCallback({ file.getAbstractPath<sideSrc>(), file.getAttributes<sideSrc>() },
@@ -1814,9 +1501,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
{
//even if the source item does not exist anymore, significant I/O work was done => report
statReporter.reportDelta(1, 0);
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>()));
-
file.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
+
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(file.getAbstractPath<sideSrc>())); //throw ThreadInterruption
}
else
throw;
@@ -1826,9 +1513,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(delHandlingTrg.getTxtRemovingFile(), AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingFile(), AFS::getDisplayPath(file.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
delHandlingTrg.removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() },
file.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
@@ -1848,9 +1535,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
const AbstractPath pathFrom = moveFrom->getAbstractPath<sideTrg>();
const AbstractPath pathTo = moveTo ->getAbstractPath<sideTrg>();
- reportInfo(txtMovingFileXtoY_, AFS::getDisplayPath(pathFrom), AFS::getDisplayPath(pathTo));
+ reportInfo(txtMovingFileXtoY_, AFS::getDisplayPath(pathFrom), AFS::getDisplayPath(pathTo)); //throw ThreadInterruption
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
//TODO: synchronizeFileInt: consider ErrorDifferentVolume! e.g. symlink aliasing!
@@ -1883,9 +1570,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
if (file.isFollowedSymlink<sideTrg>()) //follow link when updating file rather than delete it and replace with regular file!!!
targetPathResolvedOld = targetPathResolvedNew = parallel::getSymlinkResolvedPath(file.getAbstractPath<sideTrg>(), singleThread_); //throw FileError
- reportInfo(txtUpdatingFile_, AFS::getDisplayPath(targetPathResolvedOld));
+ reportInfo(txtUpdatingFile_, AFS::getDisplayPath(targetPathResolvedOld)); //throw ThreadInterruption
- AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, file.getFileSize<sideSrc>(), acb_);
if (file.isFollowedSymlink<sideTrg>()) //since we follow the link, we need to sync case sensitivity of the link manually!
if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case?
@@ -1932,9 +1619,9 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
//harmonize with file_hierarchy.cpp::getSyncOpDescription!!
- reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(file.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(file.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
assert(file.getItemName<sideTrg>() != file.getItemName<sideSrc>());
if (file.getItemName<sideTrg>() != file.getItemName<sideSrc>()) //have difference in case?
@@ -1993,8 +1680,7 @@ void FolderPairSyncer::synchronizeLink(SymlinkPair& link) //throw FileError, Thr
template <SelectedSide sideTrg>
void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
- warn_static("test constexpr compiler conformance")
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
@@ -2007,9 +1693,9 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
return; //if parent directory creation failed, there's no reason to show more errors!
const AbstractPath targetPath = symlink.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingLink_, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingLink_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
try
{
parallel::copySymlink(symlink.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_, singleThread_); //throw FileError
@@ -2033,9 +1719,9 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
{
//even if the source item does not exist anymore, significant I/O work was done => report
statReporter.reportDelta(1, 0);
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(symlink.getAbstractPath<sideSrc>()));
-
symlink.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!)
+
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(symlink.getAbstractPath<sideSrc>())); //throw ThreadInterruption
}
else
throw;
@@ -2045,9 +1731,9 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(delHandlingTrg.getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
delHandlingTrg.removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
@@ -2057,9 +1743,9 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
case SO_OVERWRITE_LEFT:
case SO_OVERWRITE_RIGHT:
- reportInfo(txtUpdatingLink_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingLink_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
//reportStatus(delHandlingTrg.getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
delHandlingTrg.removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
@@ -2085,9 +1771,9 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
if (symlink.getItemName<sideTrg>() != symlink.getItemName<sideSrc>()) //have difference in case?
parallel::renameItem(symlink.getAbstractPath<sideTrg>(), //throw FileError, (ErrorDifferentVolume)
@@ -2140,7 +1826,7 @@ void FolderPairSyncer::synchronizeFolder(FolderPair& folder) //throw FileError,
template <SelectedSide sideTrg>
void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation syncOp) //throw FileError, ThreadInterruption
{
- static const SelectedSide sideSrc = OtherSide<sideTrg>::result;
+ constexpr SelectedSide sideSrc = OtherSide<sideTrg>::value;
DeletionHandling& delHandlingTrg = SelectParam<sideTrg>::ref(delHandlingLeft_, delHandlingRight_);
switch (syncOp)
@@ -2153,12 +1839,12 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy
return; //if parent directory creation failed, there's no reason to show more errors!
const AbstractPath targetPath = folder.getAbstractPath<sideTrg>();
- reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath));
+ reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
//shallow-"copying" a folder might not fail if source is missing, so we need to check this first:
if (parallel::getItemTypeIfExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
try
{
//target existing: undefined behavior! (fail/overwrite)
@@ -2184,27 +1870,28 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy
else //source deleted meanwhile...
{
const SyncStatistics subStats(folder);
- AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), acb_);
//even if the source item does not exist anymore, significant I/O work was done => report
statReporter.reportDelta(1, 0);
- reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>()));
//remove only *after* evaluating folder!!
folder.refSubFiles ().clear(); //
folder.refSubLinks ().clear(); //update FolderPair
folder.refSubFolders().clear(); //
folder.removeObject<sideSrc>(); //
+
+ reportInfo(txtSourceItemNotFound_, AFS::getDisplayPath(folder.getAbstractPath<sideSrc>())); //throw ThreadInterruption
}
}
break;
case SO_DELETE_LEFT:
case SO_DELETE_RIGHT:
- reportInfo(delHandlingTrg.getTxtRemovingFolder(), AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
+ reportInfo(delHandlingTrg.getTxtRemovingFolder(), AFS::getDisplayPath(folder.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
const SyncStatistics subStats(folder); //counts sub-objects only!
- AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), acb_);
delHandlingTrg.removeDirWithCallback(folder.getAbstractPath<sideTrg>(), folder.getPairRelativePath(), statReporter, singleThread_); //throw FileError, X
@@ -2221,9 +1908,9 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy
case SO_OVERWRITE_RIGHT: //
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
- reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(folder.getAbstractPath<sideTrg>()));
+ reportInfo(txtUpdatingAttributes_, AFS::getDisplayPath(folder.getAbstractPath<sideTrg>())); //throw ThreadInterruption
{
- AsyncItemStatReporter statReporter(1, 0, threadIdx_, acb_);
+ AsyncItemStatReporter statReporter(1, 0, acb_);
assert(folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>());
if (folder.getItemName<sideTrg>() != folder.getItemName<sideSrc>()) //have difference in case?
@@ -2280,7 +1967,11 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor&
onDeleteTargetFile();
}
},
- [&](int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }, //callback runs *outside* singleThread_ lock! => fine
+ [&](int64_t bytesDelta) //callback runs *outside* singleThread_ lock! => fine
+ {
+ statReporter.reportDelta(0, bytesDelta);
+ interruptionPoint(); //throw ThreadInterruption
+ },
singleThread_);
//#################### Verification #############################
@@ -2289,7 +1980,7 @@ AFS::FileCopyResult FolderPairSyncer::copyFileWithCallback(const FileDescriptor&
ZEN_ON_SCOPE_FAIL(try { parallel::removeFilePlain(targetPath, singleThread_); }
catch (FileError&) {}); //delete target if verification fails
- reportInfo(txtVerifyingFile_, AFS::getDisplayPath(targetPath));
+ reportInfo(txtVerifyingFile_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
//callback runs *outside* singleThread_ lock! => fine
auto verifyCallback = [&](int64_t bytesDelta) { interruptionPoint(); /*throw ThreadInterruption*/ };
@@ -2312,28 +2003,32 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process
const AbstractPath folderPath = baseFolder.getAbstractPath<side>();
if (baseFolder.isAvailable<side>())
- if (Opt<std::wstring> errMsg = tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking({ folderPath }, {} /*deviceParallelOps*/,
- folderAccessTimeout, false /*allowUserInteraction*/, callback);
+ const std::wstring errMsg = tryReportingError([&]
+ {
+ const FolderStatus status = getFolderStatusNonBlocking({ folderPath }, {} /*deviceParallelOps*/,
+ folderAccessTimeout, false /*allowUserInteraction*/, callback);
- static_assert(IsSameType<decltype(status.failedChecks.begin()->second), FileError>::value, "");
+ static_assert(std::is_same_v<decltype(status.failedChecks.begin()->second), FileError>);
if (!status.failedChecks.empty())
throw status.failedChecks.begin()->second;
if (status.existing.find(folderPath) == status.existing.end())
throw FileError(replaceCpy(_("Cannot find folder %x."), L"%x", fmtPath(AFS::getDisplayPath(folderPath))));
//should really be logged as a "fatal error" if ignored by the user...
- }, callback)) //throw X
- return true;
+ }, callback); //throw X
+ if (!errMsg.empty())
+ return true;
+ }
return false;
}
template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying!
-bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) //return false if fatal error occurred
+bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, int folderAccessTimeout, ProcessCallback& callback) //return false if fatal error occurred
{
+ static const SelectedSide sideSrc = OtherSide<side>::value;
const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>();
if (AFS::isNullPath(baseFolderPath))
@@ -2342,18 +2037,28 @@ bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, Proce
if (!baseFolder.isAvailable<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically
{
bool temporaryNetworkDrop = false;
- zen::Opt<std::wstring> errMsg = tryReportingError([&]
+ const std::wstring errMsg = tryReportingError([&]
{
const FolderStatus status = getFolderStatusNonBlocking({ baseFolderPath }, {} /*deviceParallelOps*/,
folderAccessTimeout, false /*allowUserInteraction*/, callback);
- static_assert(IsSameType<decltype(status.failedChecks.begin()->second), FileError>::value, "");
+ static_assert(std::is_same_v<decltype(status.failedChecks.begin()->second), FileError>);
if (!status.failedChecks.empty())
throw status.failedChecks.begin()->second;
if (status.notExisting.find(baseFolderPath) != status.notExisting.end())
{
- AFS::createFolderIfMissingRecursion(baseFolderPath); //throw FileError
+ if (baseFolder.isAvailable<sideSrc>()) //copy file permissions
+ {
+ if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(baseFolderPath))
+ if (AFS::getParentFolderPath(*parentPath)) //not device root
+ AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError
+
+ AFS::copyNewFolder(baseFolder.getAbstractPath<sideSrc>(), baseFolderPath, copyFilePermissions); //throw FileError
+ }
+ else
+ AFS::createFolderIfMissingRecursion(baseFolderPath); //throw FileError
+
baseFolder.setAvailable<side>(true); //update our model!
}
else
@@ -2369,7 +2074,7 @@ bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, Proce
// 3. log file creates containing folder -> no, log only created in batch mode, and only *before* comparison
}
}, callback); //throw X
- return !errMsg && !temporaryNetworkDrop;
+ return errMsg.empty() && !temporaryNetworkDrop;
}
return true;
}
@@ -2434,7 +2139,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
}
catch (const FileError& e) //not an error in this context
{
- callback.reportInfo(e.toString()); //may throw!
+ callback.reportInfo(e.toString()); //throw X
}
//prevent operating system going into sleep state
@@ -2445,7 +2150,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
}
catch (const FileError& e) //not an error in this context
{
- callback.reportInfo(e.toString()); //may throw!
+ callback.reportInfo(e.toString()); //throw X
}
//-------------------execute basic checks all at once before starting sync--------------------------------------
@@ -2600,7 +2305,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
if (!AFS::isNullPath(baseFolderPath))
if (recyclerSupported.find(baseFolderPath) == recyclerSupported.end()) //perf: avoid duplicate checks!
{
- callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x",
+ callback.reportStatus(replaceCpy(_("Checking recycle bin availability for folder %x..."), L"%x", //throw X
fmtPath(AFS::getDisplayPath(baseFolderPath))));
bool recSupported = false;
tryReportingError([&]
@@ -2745,7 +2450,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
continue;
//------------------------------------------------------------------------------------------
- callback.reportInfo(_("Synchronizing folder pair:") + L" " + getVariantNameForLog(folderPairCfg.syncVariant) + L"\n" +
+ callback.reportInfo(_("Synchronizing folder pair:") + L" " + getVariantNameForLog(folderPairCfg.syncVariant) + L"\n" + //throw X
L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>()) + L"\n" +
L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>()));
//------------------------------------------------------------------------------------------
@@ -2757,8 +2462,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//create base folders if not yet existing
if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
- if (!createBaseFolder< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) || //+ detect temporary network drop!!
- !createBaseFolder<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback)) //
+ if (!createBaseFolder< LEFT_SIDE>(baseFolder, copyFilePermissions, folderAccessTimeout, callback) || //+ detect temporary network drop!!
+ !createBaseFolder<RIGHT_SIDE>(baseFolder, copyFilePermissions, folderAccessTimeout, callback)) //
continue;
//------------------------------------------------------------------------------------------
@@ -2864,7 +2569,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//(try to gracefully) write database file
if (folderPairCfg.saveSyncDB)
{
- callback.reportStatus(_("Generating database..."));
+ callback.reportStatus(_("Generating database...")); //throw X
callback.forceUiRefresh(); //throw X
tryReportingError([&]
diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/base/synchronization.h
index 6e09709c..4e5173c5 100755
--- a/FreeFileSync/Source/synchronization.h
+++ b/FreeFileSync/Source/base/synchronization.h
@@ -9,7 +9,7 @@
#include <chrono>
#include "file_hierarchy.h"
-#include "lib/process_xml.h"
+#include "process_xml.h"
#include "process_callback.h"
diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp
index 9fb2804f..e3972d9f 100755
--- a/FreeFileSync/Source/lib/versioning.cpp
+++ b/FreeFileSync/Source/base/versioning.cpp
@@ -1,5 +1,5 @@
#include "versioning.h"
-#include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t
+//#include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t
using namespace zen;
using namespace fff;
@@ -114,7 +114,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
const AFS::PathStatus& ps = *psTmp;
//previous exception contains context-level information, but current exception is the immediate problem => combine both
//=> e.g. prevEx might be about missing parent folder; FFS considers session faulty and tries to create a new one,
- //which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED (due to limit on #sessions?) https://www.freefilesync.org/forum/viewtopic.php?t=4765#p16016
+ //which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED (due to limit on #sessions?) https://freefilesync.org/forum/viewtopic.php?t=4765#p16016
if (ps.relPath.empty()) //already existing
{
@@ -280,8 +280,7 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst
//create target directories only when needed in moveFileToVersioning(): avoid empty directories!
auto ft = std::make_shared<FlatTraverserCallback>(folderPath); //traverse source directory one level deep
- const AFS::PathComponents pc = AFS::getPathComponents(folderPath);
- AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, ft }}, 1 /*parallelOps*/); //throw FileError
+ AFS::traverseFolderParallel(folderPath, {{ {}, ft }}, 1 /*parallelOps*/); //throw FileError
const Zstring relPathPf = appendSeparator(relativePath);
diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/base/versioning.h
index 78a031a0..ca0e4869 100755
--- a/FreeFileSync/Source/lib/versioning.h
+++ b/FreeFileSync/Source/base/versioning.h
@@ -10,9 +10,9 @@
#include <functional>
#include <zen/time.h>
#include <zen/file_error.h>
-#include "../structures.h"
+#include "structures.h"
+#include "algorithm.h"
#include "../fs/abstract.h"
-#include "../algorithm.h"
namespace fff
diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp
index 1b857161..34404fca 100755
--- a/FreeFileSync/Source/fs/abstract.cpp
+++ b/FreeFileSync/Source/fs/abstract.cpp
@@ -69,15 +69,12 @@ Opt<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath)
}
-void AFS::traverseFolderParallel(const AbstractPath& rootPath, const AFS::TraverserWorkload& workload, size_t parallelOps)
+void AFS::traverseFolderParallel(const AbstractPath& basePath, const AFS::TraverserWorkload& workload, size_t parallelOps)
{
- warn_static("just glue to rootPath!")
- assert(rootPath.afsPath.value.empty());
-
TraverserWorkloadImpl wlImpl;
for (const auto& item : workload)
{
- AfsPath afsPath;
+ AfsPath afsPath = basePath.afsPath;
for (const Zstring& itemName : item.first)
{
assert(!contains(itemName, FILE_NAME_SEPARATOR));
@@ -87,7 +84,7 @@ void AFS::traverseFolderParallel(const AbstractPath& rootPath, const AFS::Traver
}
wlImpl.emplace_back(afsPath, item.second);
}
- rootPath.afs->traverseFolderParallel(wlImpl, parallelOps); //throw
+ basePath.afs->traverseFolderParallel(wlImpl, parallelOps); //throw
}
@@ -128,8 +125,8 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St
/*
is setting modtime after closing the file handle a pessimization?
Native: no, needed for functional correctness, see file_access.cpp
- MTP: maybe a minor one (need to retrieve objectId one more time)
- SFTP: no, needed for functional correctness, just as for Native
+ MTP: maybe a minor one (need to determine objectId one more time)
+ SFTP: no, needed for functional correctness (synology server), just as for Native
FTP: maybe a minor one: could set modtime via CURLOPT_POSTQUOTE (but this would internally trigger an extra round-trip anyway!)
*/
setModTime(apTarget, attrSourceNew.modTime); //throw FileError, follows symlinks
@@ -138,12 +135,11 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St
{
/*
Failing to set modification time is not a serious problem from synchronization perspective (treated like external update)
-
- => Support additional scenarios:
- - GVFS failing to set modTime for FTP: https://www.freefilesync.org/forum/viewtopic.php?t=2372
- - GVFS failing to set modTime for MTP: https://www.freefilesync.org/forum/viewtopic.php?t=2803
- - MTP failing to set modTime in general: fail non-silently rather than silently during file creation
- - FTP failing to set modTime for servers lacking MFMT-support
+ => Support additional scenarios:
+ - GVFS failing to set modTime for FTP: https://freefilesync.org/forum/viewtopic.php?t=2372
+ - GVFS failing to set modTime for MTP: https://freefilesync.org/forum/viewtopic.php?t=2803
+ - MTP failing to set modTime in general: fail non-silently rather than silently during file creation
+ - FTP failing to set modTime for servers lacking MFMT-support
*/
errorModTime = FileError(e.toString()); //avoid slicing
}
@@ -191,7 +187,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
const Zstring fileName = AFS::getItemName(apTarget);
//- generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file
- //- do not loop and avoid pathological cases, e.g. https://www.freefilesync.org/forum/viewtopic.php?t=1592
+ //- do not loop and avoid pathological cases, e.g. https://freefilesync.org/forum/viewtopic.php?t=1592
const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID())));
auto it = find_last(fileName.begin(), fileName.end(), Zchar('.')); //gracefully handle case of missing "."
const Zstring fileNameTmp = Zstring(fileName.begin(), it) + Zchar('.') + shortGuid + TEMP_FILE_ENDING;
@@ -377,8 +373,7 @@ void removeFolderIfExistsRecursionImpl(const AbstractPath& folderPath, //throw F
//deferred recursion => save stack space and allow deletion of extremely deep hierarchies!
auto ft = std::make_shared<FlatTraverserCallback>(folderPath);
- const AFS::PathComponents pc = AFS::getPathComponents(folderPath);
- AFS::traverseFolderParallel(pc.rootPath, {{ pc.relPath, ft }}, 1 /*parallelOps*/); //throw FileError
+ AFS::traverseFolderParallel(folderPath, {{ {}, ft }}, 1 /*parallelOps*/); //throw FileError
for (const Zstring& fileName : ft->refFileNames())
{
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h
index 9774aead..9ca897f7 100755
--- a/FreeFileSync/Source/fs/abstract.h
+++ b/FreeFileSync/Source/fs/abstract.h
@@ -11,7 +11,6 @@
#include <zen/file_error.h>
#include <zen/zstring.h>
#include <zen/optional.h>
-#include <zen/thread.h>
#include <zen/serialize.h> //InputStream/OutputStream support buffered stream concept
#include <wx+/image_holder.h> //NOT a wxWidgets dependency!
@@ -226,7 +225,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
using TraverserWorkload = std::vector<std::pair<std::vector<Zstring> /*relPath*/, std::shared_ptr<TraverserCallback> /*throw X*/>>;
//- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...)
- static void traverseFolderParallel(const AbstractPath& rootPath, const TraverserWorkload& workload, size_t parallelOps);
+ static void traverseFolderParallel(const AbstractPath& basePath, const TraverserWorkload& workload, size_t parallelOps);
//----------------------------------------------------------------------------------------------------------------
@@ -393,227 +392,6 @@ inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { retur
inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { return !(lhs == rhs); }
-//implement "retry" in a generic way:
-template <class Command> inline //function object expecting to throw FileError if operation fails
-bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& callback) //throw X, return "true" on success, "false" if error was ignored
-{
- for (size_t retryNumber = 0;; ++retryNumber)
- try
- {
- cmd(); //throw FileError
- return true;
- }
- catch (const zen::FileError& e)
- {
- switch (callback.reportDirError(e.toString(), retryNumber)) //throw X
- {
- case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
- break;
- case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
- return false;
- }
- }
-}
-
-
-template <class Command> inline //function object expecting to throw FileError if operation fails
-bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& callback, const Zstring& itemName) //throw X, return "true" on success, "false" if error was ignored
-{
- for (size_t retryNumber = 0;; ++retryNumber)
- try
- {
- cmd(); //throw FileError
- return true;
- }
- catch (const zen::FileError& e)
- {
- switch (callback.reportItemError(e.toString(), retryNumber, itemName)) //throw X
- {
- case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
- break;
- case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
- return false;
- }
- }
-}
-
-
-#if __GNUC__ < 6 //support for "fold expressions" requires GCC 6.0 or later: http://en.cppreference.com/w/cpp/compiler_support
- #define ZEN_LINUX_TRAVERSER_LEGACY
-#else
- #define ZEN_LINUX_TRAVERSER_MODERN
-#endif
-
-#if __GNUC__ == 6 //std::apply available with GCC 7
-}
-namespace std
-{
-template <class F, class T0, class T1, class T2>
-constexpr decltype(auto) apply(F&& f, std::tuple<T0, T1, T2>& t) { return std::invoke(std::forward<F>(f), std::get<0>(t), std::get<1>(t), std::get<2>(t)); }
-}
-namespace fff
-{
-#endif
-
-
-#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
-template <class Function>
-struct WorkItem
-{
- Function getResult; //throw FileError
- Zstring errorItemName; //empty if all items affected
- size_t errorRetryCount = 0;
- std::shared_ptr<AbstractFileSystem::TraverserCallback> cb; //call by controlling thread only! => don't require traverseFolderParallel() callbacks to be thread-safe!
-};
-
-template <class Function>
-struct ResultItem
-{
- WorkItem<Function> wi;
- std::exception_ptr error; //mutually exclusive
- decltype(wi.getResult()) value; //
-};
-
-
-template <class... Functions> //avoid std::function memory alloc + virtual calls
-class AsyncTraverserWorkload
-{
-public:
- AsyncTraverserWorkload() {}
-
- //context of controlling thread, non-blocking:
- //NOTE: AsyncTraverserWorkload must out-live threadGroup!!!
- template <class Function>
- void run(WorkItem<Function>&& wi, zen::ThreadGroup<std::function<void()>>& threadGroup)
- {
- threadGroup.run([this, wi]
- {
- try { this->returnResult<Function>({ wi, nullptr, wi.getResult() }); } //throw FileError
- catch (...) { this->returnResult<Function>({ wi, std::current_exception(), {} }); }
- });
-
- std::lock_guard<std::mutex> dummy(lockWork_);
- ++workItemsInProcess_;
- }
-
- //context of controlling thread, blocking:
- bool getResults(std::tuple<std::vector<ResultItem<Functions>>...>& results) //returns false when traversal is finished
- {
- std::apply([](auto&... r) { (..., r.clear()); }, results);
-
- std::unique_lock<std::mutex> dummy(lockWork_);
-
- auto resultsReady = [&]
- {
- bool ready = false;
- std::apply([&ready](const auto&... r) { ready = (... || !r.empty()); }, results_);
- return ready;
- };
-
- if (!resultsReady() && workItemsInProcess_ == 0)
- return false;
-
- conditionNewResult_.wait(dummy, [&resultsReady] { return resultsReady(); });
-
- results.swap(results_); //reuse memory + avoid needless item-level mutex locking
- return true;
- }
-
-private:
- AsyncTraverserWorkload (const AsyncTraverserWorkload&) = delete;
- AsyncTraverserWorkload& operator=(const AsyncTraverserWorkload&) = delete;
-
- //context of worker threads, non-blocking:
- template <class Function>
- void returnResult(ResultItem<Function>&& r)
- {
- {
- std::lock_guard<std::mutex> dummy(lockWork_);
-
- std::get<std::vector<ResultItem<Function>>>(results_).push_back(std::move(r));
- --workItemsInProcess_;
- }
- conditionNewResult_.notify_all();
- }
-
- std::mutex lockWork_;
- size_t workItemsInProcess_ = 0;
- std::tuple<std::vector<ResultItem<Functions>>...> results_;
- std::condition_variable conditionNewResult_;
-};
-
-
-template <class... Functions>
-class GenericDirTraverser
-{
-public:
- using Function1 = typename zen::GetFirstOf<Functions...>::Type;
-
- GenericDirTraverser(std::vector<WorkItem<Function1>>&& initialWorkItems, size_t parallelOps, const std::string& threadGroupName) :
- threadGroup_(std::max<size_t>(1, parallelOps), threadGroupName)
- {
- //set the initial work load
- for (auto& item : initialWorkItems)
- asyncWorkload_.template run<Function1>(std::move(item), threadGroup_);
-
- //run loop
- std::tuple<std::vector<ResultItem<Functions>>...> results; //avoid per-getNextResults() memory allocations (=> swap instead!)
-
- while (asyncWorkload_.getResults(results))
- std::apply([&](auto&... r) { (..., this->evalResultList(r)); }, results); //throw X
- }
-
-private:
- GenericDirTraverser (const GenericDirTraverser&) = delete;
- GenericDirTraverser& operator=(const GenericDirTraverser&) = delete;
-
- template <class Function>
- void evalResultList(std::vector<ResultItem<Function>>& results) //throw X
- {
- for (ResultItem<Function>& result : results)
- evalResult(result); //throw X
- }
-
- template <class Function>
- void evalResult(ResultItem<Function>& result); //throw X
-
- //specialize!
- template <class Function>
- void evalResultValue(const typename Function::Result& r, std::shared_ptr<AbstractFileSystem::TraverserCallback>& cb); //throw X
-
- AsyncTraverserWorkload<Functions...> asyncWorkload_; //ATTENTION: asyncWorkload_ must out-live threadGroup_!!!
- zen::ThreadGroup<std::function<void()>> threadGroup_; //
-};
-
-
-template <class... Functions>
-template <class Function>
-void GenericDirTraverser<Functions...>::evalResult(ResultItem<Function>& result) //throw X
-{
- auto& cb = result.wi.cb;
- try
- {
- if (result.error)
- std::rethrow_exception(result.error); //throw FileError
- }
- catch (const zen::FileError& e)
- {
- switch (result.wi.errorItemName.empty() ?
- cb->reportDirError (e.toString(), result.wi.errorRetryCount) : //throw X
- cb->reportItemError(e.toString(), result.wi.errorRetryCount, result.wi.errorItemName)) //throw X
- {
- case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
- asyncWorkload_.template run<Function>({ std::move(result.wi.getResult), result.wi.errorItemName, result.wi.errorRetryCount + 1, cb }, threadGroup_);
- return;
-
- case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
- return;
- }
- }
-
- evalResultValue<Function>(result.value, result.wi.cb); //throw X
-}
-#endif
diff --git a/FreeFileSync/Source/fs/concrete_impl.h b/FreeFileSync/Source/fs/concrete_impl.h
new file mode 100644
index 00000000..5ad9da54
--- /dev/null
+++ b/FreeFileSync/Source/fs/concrete_impl.h
@@ -0,0 +1,211 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef IMPL_HELPER_H_873450978453042524534234
+#define IMPL_HELPER_H_873450978453042524534234
+
+#include "abstract.h"
+#include <zen/thread.h>
+
+
+namespace fff
+{
+template <class Function> inline //return ignored error message if available
+std::wstring tryReportingDirError(Function cmd /*throw FileError*/, AbstractFileSystem::TraverserCallback& cb /*throw X*/)
+{
+ for (size_t retryNumber = 0;; ++retryNumber)
+ try
+ {
+ cmd(); //throw FileError
+ return std::wstring();
+ }
+ catch (const zen::FileError& e)
+ {
+ assert(!e.toString().empty());
+ switch (cb.reportDirError(e.toString(), retryNumber)) //throw X
+ {
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ return e.toString();
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ break; //continue with loop
+ }
+ }
+}
+
+
+template <class Context, class Function>
+struct Task
+{
+ Function getResult; //throw FileError
+ /* [[no_unique_address]] */ Context ctx;
+};
+
+
+template <class Context, class Function>
+struct TaskResult
+{
+ Task<Context, Function> wi;
+ std::exception_ptr error; //mutually exclusive
+ decltype(wi.getResult()) value; //
+};
+
+enum class SchedulerStatus
+{
+ HAVE_RESULT,
+ FINISHED,
+};
+
+template <class Context, class... Functions> //avoid std::function memory alloc + virtual calls
+class TaskScheduler
+{
+public:
+ TaskScheduler(size_t threadCount, const std::string& groupName) :
+ threadGroup_(zen::ThreadGroup<std::function<void()>>(threadCount, groupName)) {}
+
+ ~TaskScheduler() { threadGroup_ = zen::NoValue(); } //TaskScheduler must out-live threadGroup! (captured "this")
+
+ //context of controlling thread, non-blocking:
+ template <class Function>
+ void run(Task<Context, Function>&& wi)
+ {
+ threadGroup_->run([this, wi = std::move(wi)]
+ {
+ try { this->returnResult<Function>({ wi, nullptr, wi.getResult() }); } //throw FileError
+ catch (...) { this->returnResult<Function>({ wi, std::current_exception(), {} }); }
+ });
+
+ std::lock_guard<std::mutex> dummy(lockResult_);
+ ++resultsPending_;
+ }
+
+ //context of controlling thread, blocking:
+ SchedulerStatus getResults(std::tuple<std::vector<TaskResult<Context, Functions>>...>& results)
+ {
+ std::apply([](auto&... r) { (..., r.clear()); }, results);
+
+ std::unique_lock<std::mutex> dummy(lockResult_);
+
+ auto resultsReady = [&]
+ {
+ bool ready = false;
+ std::apply([&ready](const auto&... r) { ready = (... || !r.empty()); }, results_);
+ return ready;
+ };
+
+ if (!resultsReady() && resultsPending_ == 0)
+ return SchedulerStatus::FINISHED;
+
+ conditionNewResult_.wait(dummy, [&resultsReady] { return resultsReady(); });
+
+ results.swap(results_); //reuse memory + avoid needless item-level mutex locking
+ return SchedulerStatus::HAVE_RESULT;
+ }
+
+private:
+ TaskScheduler (const TaskScheduler&) = delete;
+ TaskScheduler& operator=(const TaskScheduler&) = delete;
+
+ //context of worker threads, non-blocking:
+ template <class Function>
+ void returnResult(TaskResult<Context, Function>&& r)
+ {
+ {
+ std::lock_guard<std::mutex> dummy(lockResult_);
+
+ std::get<std::vector<TaskResult<Context, Function>>>(results_).push_back(std::move(r));
+ --resultsPending_;
+ }
+ conditionNewResult_.notify_all();
+ }
+
+ zen::Opt<zen::ThreadGroup<std::function<void()>>> threadGroup_;
+
+ std::mutex lockResult_;
+ size_t resultsPending_ = 0;
+ std::tuple<std::vector<TaskResult<Context, Functions>>...> results_;
+ std::condition_variable conditionNewResult_;
+};
+
+
+struct TravContext
+{
+ Zstring errorItemName; //empty if all items affected
+ size_t errorRetryCount = 0;
+ std::shared_ptr<AbstractFileSystem::TraverserCallback> cb; //call by controlling thread only! => don't require traverseFolderParallel() callbacks to be thread-safe!
+};
+
+
+template <class... Functions>
+class GenericDirTraverser
+{
+public:
+ using Function1 = zen::GetFirstOfT<Functions...>;
+
+ GenericDirTraverser(std::vector<Task<TravContext, Function1>>&& initialTasks, size_t parallelOps, const std::string& threadGroupName) :
+ scheduler_(parallelOps, threadGroupName)
+ {
+ //set the initial work load
+ for (auto& item : initialTasks)
+ scheduler_.template run<Function1>(std::move(item));
+
+ //run loop
+ std::tuple<std::vector<TaskResult<TravContext, Functions>>...> results; //avoid per-getNextResults() memory allocations (=> swap instead!)
+
+ while (scheduler_.getResults(results) == SchedulerStatus::HAVE_RESULT)
+ std::apply([&](auto&... r) { (..., this->evalResultList(r)); }, results); //throw X
+ }
+
+private:
+ GenericDirTraverser (const GenericDirTraverser&) = delete;
+ GenericDirTraverser& operator=(const GenericDirTraverser&) = delete;
+
+ template <class Function>
+ void evalResultList(std::vector<TaskResult<TravContext, Function>>& results) //throw X
+ {
+ for (TaskResult<TravContext, Function>& result : results)
+ evalResult(result); //throw X
+ }
+
+ template <class Function>
+ void evalResult(TaskResult<TravContext, Function>& result); //throw X
+
+ //specialize!
+ template <class Function>
+ void evalResultValue(const typename Function::Result& r, std::shared_ptr<AbstractFileSystem::TraverserCallback>& cb); //throw X
+
+ TaskScheduler<TravContext, Functions...> scheduler_;
+};
+
+
+template <class... Functions>
+template <class Function>
+void GenericDirTraverser<Functions...>::evalResult(TaskResult<TravContext, Function>& result) //throw X
+{
+ auto& cb = result.wi.ctx.cb;
+ try
+ {
+ if (result.error)
+ std::rethrow_exception(result.error); //throw FileError
+ }
+ catch (const zen::FileError& e)
+ {
+ switch (result.wi.ctx.errorItemName.empty() ?
+ cb->reportDirError (e.toString(), result.wi.ctx.errorRetryCount) : //throw X
+ cb->reportItemError(e.toString(), result.wi.ctx.errorRetryCount, result.wi.ctx.errorItemName)) //throw X
+ {
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
+ scheduler_.template run<Function>({ std::move(result.wi.getResult), TravContext{ result.wi.ctx.errorItemName, result.wi.ctx.errorRetryCount + 1, cb }});
+ return;
+
+ case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
+ return;
+ }
+ }
+ evalResultValue<Function>(result.value, cb); //throw X
+}
+}
+
+#endif //IMPL_HELPER_H_873450978453042524534234
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index 864b1369..de8e0e7c 100755
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -14,8 +14,9 @@
#include <zen/thread.h>
#include <zen/guid.h>
#include <zen/crc.h>
-#include "../lib/resolve_path.h"
-#include "../lib/icon_loader.h"
+#include "concrete_impl.h"
+#include "../base/resolve_path.h"
+#include "../base/icon_loader.h"
#include <cstddef> //offsetof
@@ -49,7 +50,6 @@ AFS::FileId convertToAbstractFileId(const zen::FileId& fid)
}
-#if !defined ZEN_LINUX || defined ZEN_LINUX_TRAVERSER_MODERN
struct FsItemRaw
{
Zstring itemName;
@@ -198,13 +198,13 @@ private:
};
-void traverseFolderParallelNative(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps) //throw X
+void traverseFolderParallelNative(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialTasks, size_t parallelOps) //throw X
{
- std::vector<WorkItem<GetDirDetails>> genItems;
+ std::vector<Task<TravContext, GetDirDetails>> genItems;
- for (const auto& item : initialWorkItems)
+ for (const auto& item : initialTasks)
genItems.push_back({ GetDirDetails(item.first),
- Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, item.second /*TraverserCallback*/ });
+ TravContext{ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, item.second /*TraverserCallback*/ }});
GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>(std::move(genItems), parallelOps, "Native Traverser"); //throw X
}
@@ -217,8 +217,7 @@ template <>
void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetDirDetails>(const GetDirDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb) //throw X
{
for (const FsItemRaw& rawItem : r)
- asyncWorkload_.run<GetItemDetails>({ GetItemDetails(rawItem),
- rawItem.itemName, 0 /*errorRetryCount*/, cb }, threadGroup_);
+ scheduler_.run<GetItemDetails>({ GetItemDetails(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }});
}
@@ -234,16 +233,14 @@ void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::e
case ItemType::FOLDER:
if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb->onFolder({ r.raw.itemName, nullptr /*symlinkInfo*/ })) //throw X
- asyncWorkload_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath),
- Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }, threadGroup_);
+ scheduler_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath), TravContext{ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }});
break;
case ItemType::SYMLINK:
switch (cb->onSymlink({ r.raw.itemName, r.details.modTime })) //throw X
{
case AFS::TraverserCallback::LINK_FOLLOW:
- asyncWorkload_.run<GetLinkTargetDetails>({ GetLinkTargetDetails(r.raw, r.details),
- r.raw.itemName, 0 /*errorRetryCount*/, cb }, threadGroup_);
+ scheduler_.run<GetLinkTargetDetails>({ GetLinkTargetDetails(r.raw, r.details), TravContext{ r.raw.itemName, 0 /*errorRetryCount*/, cb }});
break;
case AFS::TraverserCallback::LINK_SKIP:
@@ -265,8 +262,7 @@ void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::e
if (r.target.type == ItemType::FOLDER)
{
if (std::shared_ptr<AFS::TraverserCallback> cbSub = cb->onFolder({ r.raw.itemName, &linkInfo })) //throw X
- asyncWorkload_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath),
- Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }, threadGroup_);
+ scheduler_.run<GetDirDetails>({ GetDirDetails(r.raw.itemPath), TravContext{ Zstring() /*errorItemName*/, 0 /*errorRetryCount*/, std::move(cbSub) }});
}
else //a file or named pipe, ect.
cb->onFile({ r.raw.itemName, r.target.fileSize, r.target.modTime, convertToAbstractFileId(r.target.fileId), &linkInfo }); //throw X
@@ -275,165 +271,6 @@ void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::e
namespace
{
-#elif defined ZEN_LINUX && defined ZEN_LINUX_TRAVERSER_LEGACY //support legacy GCC versions < 6
-static_assert(__GNUC__ < 6, "");
-
-class LegacyDirTraverser
-{
-public:
- static void execute(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps)
- {
- assert(parallelOps == 1);
- //Parallel operations > 1 are not supported because FreeFileSync was built with GCC < version 6.",
- // => at least parallel_scan.h will show the correct number of threads
-
- for (const auto& item : initialWorkItems)
- LegacyDirTraverser(item.first, *item.second); //throw X
- }
-
-private:
- LegacyDirTraverser (const LegacyDirTraverser&) = delete;
- LegacyDirTraverser& operator=(const LegacyDirTraverser&) = delete;
-
- LegacyDirTraverser(const Zstring& baseDirPath, AFS::TraverserCallback& cb)
- {
- //set the initial work load
- workload_.push_back({ baseDirPath, std::shared_ptr<AFS::TraverserCallback>(&cb, [](AFS::TraverserCallback*) {}) });
-
- while (!workload_.empty())
- {
- WorkItem wi = std::move(workload_. back()); //yes, no strong exception guarantee (std::bad_alloc)
- /**/ workload_.pop_back(); //
-
- tryReportingDirError([&] //throw X
- {
- traverseWithException(wi.dirPath, *wi.cb); //throw FileError, X
- }, *wi.cb);
- }
- }
-
- void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& cb) //throw FileError, X
- {
- //no need to check for endless recursion:
- //1. Linux has a fixed limit on the number of symbolic links in a path
- //2. fails with "too many open files" or "path too long" before reaching stack overflow
-
- DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
- if (!folder)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
- ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash
-
- for (;;)
- {
- /*
- Linux:
- http://man7.org/linux/man-pages/man3/readdir_r.3.html
- "It is recommended that applications use readdir(3) instead of readdir_r"
- "... in modern implementations (including the glibc implementation), concurrent calls to readdir(3) that specify different directory streams are thread-safe"
-
- macOS:
- - libc: readdir thread-safe already in code from 2000: https://opensource.apple.com/source/Libc/Libc-166/gen.subproj/readdir.c.auto.html
- - and in the latest version from 2017: https://opensource.apple.com/source/Libc/Libc-1244.30.3/gen/FreeBSD/readdir.c.auto.html
- */
- errno = 0;
- const struct ::dirent* dirEntry = ::readdir(folder);
- if (!dirEntry)
- {
- if (errno == 0) //errno left unchanged => no more items
- return;
-
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir");
- //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
- }
-
- const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion
-
- //skip "." and ".."
- if (itemNameRaw[0] == '.' &&
- (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
- continue;
-
- const Zstring& itemName = itemNameRaw;
- if (itemName.empty()) //checks result of normalizeUtfForPosix, too!
- throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir: Data corruption; item with empty name.");
-
- const Zstring& itemPath = appendSeparator(dirPath) + itemName;
-
- struct ::stat statData = {};
- if (!tryReportingItemError([&] //throw X
- {
- if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
- }, cb, itemName))
- continue; //ignore error: skip file
-
- if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
- {
- const AFS::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime };
-
- switch (cb.onSymlink(linkInfo)) //throw X
- {
- case AFS::TraverserCallback::LINK_FOLLOW:
- {
- //try to resolve symlink (and report error on failure!!!)
- struct ::stat statDataTrg = {};
-
- const bool validLink = tryReportingItemError([&] //throw X
- {
- if (::stat(itemPath.c_str(), &statDataTrg) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(itemPath)), L"stat");
- }, cb, itemName);
-
- if (validLink)
- {
- if (S_ISDIR(statDataTrg.st_mode)) //a directory
- {
- if (std::shared_ptr<AFS::TraverserCallback> trav = cb.onFolder({ itemName, &linkInfo })) //throw X
- workload_.push_back({ itemPath, std::move(trav) });
- }
- else //a file or named pipe, ect.
- {
- AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo };
- cb.onFile(fi); //throw X
- }
- }
- // else //broken symlink -> ignore: it's client's responsibility to handle error!
- }
- break;
-
- case AFS::TraverserCallback::LINK_SKIP:
- break;
- }
- }
- else if (S_ISDIR(statData.st_mode)) //a directory
- {
- if (std::shared_ptr<AFS::TraverserCallback> trav = cb.onFolder({ itemName, nullptr })) //throw X
- workload_.push_back({ itemPath, std::move(trav) });
- warn_static("workload_ item order is reversed => fix!?")
- }
- else //a file or named pipe, ect. => dont't check using S_ISREG(): see comment in file_traverser.cpp
- {
- AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ };
- cb.onFile(fi); //throw X
- }
- }
- }
-
- struct WorkItem
- {
- Zstring dirPath;
- std::shared_ptr<AFS::TraverserCallback> cb;
- };
-
- std::vector<WorkItem> workload_;
-};
-
-
-void traverseFolderParallelNative(const std::vector<std::pair<Zstring, std::shared_ptr<AFS::TraverserCallback>>>& initialWorkItems, size_t parallelOps) //throw X
-{
- LegacyDirTraverser::execute(initialWorkItems, parallelOps); //throw X
-}
-#endif
//====================================================================================================
//====================================================================================================
diff --git a/FreeFileSync/Source/lib/status_handler_impl.h b/FreeFileSync/Source/lib/status_handler_impl.h
deleted file mode 100755
index 6404c915..00000000
--- a/FreeFileSync/Source/lib/status_handler_impl.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STATUS_HANDLER_IMPL_H_07682758976
-#define STATUS_HANDLER_IMPL_H_07682758976
-
-#include <zen/optional.h>
-#include <zen/file_error.h>
-#include "../process_callback.h"
-
-
-namespace fff
-{
-template <typename Function> inline
-zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& cb /*throw X*/) //return ignored error message if available
-{
- for (size_t retryNumber = 0;; ++retryNumber)
- try
- {
- cmd(); //throw FileError
- return zen::NoValue();
- }
- catch (zen::FileError& error)
- {
- switch (cb.reportError(error.toString(), retryNumber)) //throw X
- {
- case ProcessCallback::IGNORE_ERROR:
- return error.toString();
- case ProcessCallback::RETRY:
- break; //continue with loop
- }
- }
-}
-
-
-//manage statistics reporting for a single item of work
-class ItemStatReporter
-{
-public:
- ItemStatReporter(int itemsExpected, int64_t bytesExpected, ProcessCallback& cb) :
- itemsExpected_(itemsExpected),
- bytesExpected_(bytesExpected),
- cb_(cb) {}
-
- ~ItemStatReporter()
- {
- const bool scopeFail = getUncaughtExceptionCount() > exeptionCount_;
- if (scopeFail)
- cb_.updateDataTotal(itemsReported_, bytesReported_); //=> unexpected increase of total workload
- else
- //update statistics to consider the real amount of data, e.g. more than the "file size" for ADS streams,
- //less for sparse and compressed files, or file changed in the meantime!
- cb_.updateDataTotal(itemsReported_ - itemsExpected_, bytesReported_ - bytesExpected_); //noexcept!
- }
-
- void reportStatus(const std::wstring& text) { cb_.reportStatus(text); } //throw X
-
- void reportDelta(int itemsDelta, int64_t bytesDelta) //throw X
- {
- cb_.updateDataProcessed(itemsDelta, bytesDelta); //nothrow!
- itemsReported_ += itemsDelta;
- bytesReported_ += bytesDelta;
-
- //special rule: avoid temporary statistics mess up, even though they are corrected anyway below:
- if (itemsReported_ > itemsExpected_)
- {
- cb_.updateDataTotal(itemsReported_ - itemsExpected_, 0);
- itemsReported_ = itemsExpected_;
- }
- if (bytesReported_ > bytesExpected_)
- {
- cb_.updateDataTotal(0, bytesReported_ - bytesExpected_); //=> everything above "bytesExpected" adds to both "processed" and "total" data
- bytesReported_ = bytesExpected_;
- }
-
- cb_.requestUiRefresh(); //throw X
- }
-
-private:
- int itemsReported_ = 0;
- int64_t bytesReported_ = 0;
- const int itemsExpected_;
- const int64_t bytesExpected_;
- ProcessCallback& cb_;
- const int exeptionCount_ = getUncaughtExceptionCount();
-};
-}
-
-#endif //STATUS_HANDLER_IMPL_H_07682758976
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp
index 54643a5c..d0135d53 100755
--- a/FreeFileSync/Source/ui/batch_config.cpp
+++ b/FreeFileSync/Source/ui/batch_config.cpp
@@ -13,8 +13,8 @@
#include <wx+/choice_enum.h>
#include "gui_generated.h"
#include "folder_selector.h"
-#include "../lib/help_provider.h"
-#include "../lib/generate_logfile.h"
+#include "../base/help_provider.h"
+#include "../base/generate_logfile.h"
using namespace zen;
diff --git a/FreeFileSync/Source/ui/batch_config.h b/FreeFileSync/Source/ui/batch_config.h
index 7dc68db5..9a6804ca 100755
--- a/FreeFileSync/Source/ui/batch_config.h
+++ b/FreeFileSync/Source/ui/batch_config.h
@@ -8,7 +8,7 @@
#define BATCH_CONFIG_H_3921674832168945
#include <wx/window.h>
-#include "../lib/process_xml.h"
+#include "../base/process_xml.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index c5541cad..66459d8d 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -10,10 +10,10 @@
#include <zen/shutdown.h>
#include <wx+/popup_dlg.h>
#include <wx/app.h>
-#include "../lib/ffs_paths.h"
-#include "../lib/resolve_path.h"
-#include "../lib/status_handler_impl.h"
-#include "../lib/generate_logfile.h"
+#include "../base/ffs_paths.h"
+#include "../base/resolve_path.h"
+#include "../base/status_handler_impl.h"
+#include "../base/generate_logfile.h"
#include "../fs/concrete.h"
using namespace zen;
@@ -39,7 +39,7 @@ std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFold
AFS::createFolderIfMissingRecursion(logFolderPath); //throw FileError
//const std::string colon = "\xcb\xb8"; //="modifier letter raised colon" => regular colon is forbidden in file names on Windows and OS X
- //=> too many issues, most notably cmd.exe is not Unicode-aware: https://www.freefilesync.org/forum/viewtopic.php?t=1679
+ //=> too many issues, most notably cmd.exe is not Unicode-aware: https://freefilesync.org/forum/viewtopic.php?t=1679
//assemble logfile name
const TimeComp tc = getLocalTime(std::chrono::system_clock::to_time_t(syncStartTime));
@@ -247,7 +247,7 @@ BatchStatusHandler::~BatchStatusHandler()
if (!commandLine.empty())
try
{
- //use ExecutionType::ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31
+ //use ExecutionType::ASYNC until there is reason not to: https://freefilesync.org/forum/viewtopic.php?t=31
shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError
}
catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); }
diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h
index f3f3cc61..baf7f102 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.h
+++ b/FreeFileSync/Source/ui/batch_status_handler.h
@@ -10,9 +10,9 @@
#include <chrono>
#include <zen/error_log.h>
#include "progress_indicator.h"
-#include "../lib/status_handler.h"
-#include "../lib/process_xml.h"
-#include "../lib/return_codes.h"
+#include "../base/status_handler.h"
+#include "../base/process_xml.h"
+#include "../base/return_codes.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp
index f6af408f..42d8ff45 100755
--- a/FreeFileSync/Source/ui/cfg_grid.cpp
+++ b/FreeFileSync/Source/ui/cfg_grid.cpp
@@ -11,8 +11,8 @@
#include <wx+/rtl.h>
#include <wx+/image_resources.h>
#include <wx/settings.h>
-#include "../lib/icon_buffer.h"
-#include "../lib/ffs_paths.h"
+#include "../base/icon_buffer.h"
+#include "../base/ffs_paths.h"
using namespace zen;
using namespace fff;
@@ -124,14 +124,14 @@ void ConfigView::sortListViewImpl()
if (lhs->second.isLastRunCfg != rhs->second.isLastRunCfg)
return lhs->second.isLastRunCfg < rhs->second.isLastRunCfg; //"last session" label should be (always) last
- return makeSortDirection(std::greater<>(), Int2Type<ascending>())(lhs->second.lastSyncTime, rhs->second.lastSyncTime);
+ return makeSortDirection(std::greater<>(), std::bool_constant<ascending>())(lhs->second.lastSyncTime, rhs->second.lastSyncTime);
//[!] ascending LAST_SYNC shows lowest "days past" first <=> highest lastSyncTime first
};
switch (sortColumn_)
{
case ColumnTypeCfg::NAME:
- std::sort(cfgListView_.begin(), cfgListView_.end(), makeSortDirection(lessCfgName, Int2Type<ascending>()));
+ std::sort(cfgListView_.begin(), cfgListView_.end(), makeSortDirection(lessCfgName, std::bool_constant<ascending>()));
break;
case ColumnTypeCfg::LAST_SYNC:
std::sort(cfgListView_.begin(), cfgListView_.end(), lessLastSync);
@@ -353,7 +353,7 @@ ConfigView& cfggrid::getDataView(Grid& grid)
{
if (auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider()))
return prov->getDataView();
- throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] cfggrid was not initialized.");
}
@@ -361,7 +361,7 @@ void cfggrid::addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bo
{
auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider());
if (!prov)
- throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] cfggrid was not initialized.");
prov->getDataView().addCfgFiles(filePaths);
grid.Refresh(); //[!] let Grid know about changed row count *before* fiddling with selection!!!
@@ -394,7 +394,7 @@ int cfggrid::getSyncOverdueDays(Grid& grid)
{
if (auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider()))
return prov->getSyncOverdueDays();
- throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] cfggrid was not initialized.");
}
@@ -402,7 +402,7 @@ void cfggrid::setSyncOverdueDays(Grid& grid, int syncOverdueDays)
{
auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider());
if (!prov)
- throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] cfggrid was not initialized.");
prov->setSyncOverdueDays(syncOverdueDays);
grid.Refresh();
diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp
index 7ff66729..29fc2080 100755
--- a/FreeFileSync/Source/ui/file_grid.cpp
+++ b/FreeFileSync/Source/ui/file_grid.cpp
@@ -18,7 +18,7 @@
#include <wx+/dc.h>
#include <wx+/image_tools.h>
#include <wx+/image_resources.h>
-#include "../file_hierarchy.h"
+#include "../base/file_hierarchy.h"
using namespace zen;
using namespace fff;
@@ -1507,6 +1507,8 @@ private:
target.GetViewStart(nullptr, &yOld);
if (yOld != y)
target.Scroll(-1, y); //empirical test Windows/Ubuntu: this call does NOT trigger a wxEVT_SCROLLWIN event, which would incorrectly set "scrollMaster" to "&target"!
+ //CAVEAT: wxScrolledWindow::Scroll() internally calls wxWindow::Update(), leading to immediate WM_PAINT handling in the target grid!
+ // an this while we're still in our WM_PAINT handler! => no recusion, fine (hopefully)
};
int y = 0;
lead->GetViewStart(nullptr, &y);
diff --git a/FreeFileSync/Source/ui/file_grid.h b/FreeFileSync/Source/ui/file_grid.h
index 3904786a..3ae06651 100755
--- a/FreeFileSync/Source/ui/file_grid.h
+++ b/FreeFileSync/Source/ui/file_grid.h
@@ -10,7 +10,7 @@
#include <wx+/grid.h>
#include "file_view.h"
#include "file_grid_attr.h"
-#include "../lib/icon_buffer.h"
+#include "../base/icon_buffer.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp
index 19a77f8c..3f96e152 100755
--- a/FreeFileSync/Source/ui/file_view.cpp
+++ b/FreeFileSync/Source/ui/file_view.cpp
@@ -5,10 +5,10 @@
// *****************************************************************************
#include "file_view.h"
-#include "sorting.h"
-#include "../synchronization.h"
#include <zen/stl_tools.h>
#include <zen/perf.h>
+#include "sorting.h"
+#include "../base/synchronization.h"
using namespace zen;
using namespace fff;
@@ -287,7 +287,7 @@ private:
No sorting: 30 ms
*/
template <class ItemPair>
- static std::vector<ItemPair*> getItemsSorted(FixedList<ItemPair>& itemList)
+ static std::vector<ItemPair*> getItemsSorted(std::list<ItemPair>& itemList)
{
std::vector<ItemPair*> output;
for (ItemPair& item : itemList)
diff --git a/FreeFileSync/Source/ui/file_view.h b/FreeFileSync/Source/ui/file_view.h
index deaf763f..c87590e7 100755
--- a/FreeFileSync/Source/ui/file_view.h
+++ b/FreeFileSync/Source/ui/file_view.h
@@ -10,7 +10,7 @@
#include <vector>
#include <unordered_map>
#include "file_grid_attr.h"
-#include "../file_hierarchy.h"
+#include "../base/file_hierarchy.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp
index 57a2a5fd..888d2a0f 100755
--- a/FreeFileSync/Source/ui/folder_history_box.cpp
+++ b/FreeFileSync/Source/ui/folder_history_box.cpp
@@ -8,7 +8,7 @@
#include <list>
#include <zen/scope_guard.h>
#include <wx+/dc.h>
-#include "../lib/resolve_path.h"
+#include "../base/resolve_path.h"
#include <gtk/gtk.h>
using namespace zen;
diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h
index 640907fa..4ac801dd 100755
--- a/FreeFileSync/Source/ui/folder_pair.h
+++ b/FreeFileSync/Source/ui/folder_pair.h
@@ -16,8 +16,8 @@
#include "folder_selector.h"
#include "small_dlgs.h"
#include "sync_cfg.h"
-#include "../lib/norm_filter.h"
-#include "../structures.h"
+#include "../base/norm_filter.h"
+#include "../base/structures.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index 66e45aae..4a311891 100755
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -14,17 +14,14 @@
#include <wx+/image_resources.h>
#include "../fs/concrete.h"
#include "../fs/native.h"
-#include "../lib/icon_buffer.h"
-
- // #include <gtk/gtk.h>
+#include "../base/icon_buffer.h"
+ using AFS = fff::AbstractFileSystem;
using namespace zen;
using namespace fff;
- using AFS = AbstractFileSystem;
-
namespace
{
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 0ee3fbd0..d7775da4 100755
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jan 23 2018)
+// C++ code generated with wxFormBuilder (version May 29 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -3920,7 +3920,7 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const
wxBoxSizer* bSizer1881;
bSizer1881 = new wxBoxSizer( wxVERTICAL );
- m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
bSizer1881->Add( m_buttonResetDialogs, 0, wxALL, 5 );
m_staticTextResetDialogs = new wxStaticText( m_panel39, wxID_ANY, _("Show all permanently hidden dialogs and warning messages again"), wxDefaultPosition, wxDefaultSize, 0 );
@@ -4142,14 +4142,16 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- wxBoxSizer* bSizer162;
- bSizer162 = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* bSizer174;
+ bSizer174 = new wxBoxSizer( wxHORIZONTAL );
+
+ bSizerMainSection = new wxBoxSizer( wxVERTICAL );
m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizer162->Add( m_bitmapLogo, 0, 0, 5 );
+ bSizerMainSection->Add( m_bitmapLogo, 0, 0, 5 );
m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer162->Add( m_staticline341, 0, wxEXPAND, 5 );
+ bSizerMainSection->Add( m_staticline341, 0, wxEXPAND, 5 );
wxBoxSizer* bSizer186;
bSizer186 = new wxBoxSizer( wxVERTICAL );
@@ -4161,68 +4163,68 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer166;
bSizer166 = new wxBoxSizer( wxHORIZONTAL );
-
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ wxBoxSizer* bSizer251;
+ bSizer251 = new wxBoxSizer( wxVERTICAL );
m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_bitmapHomepage->SetToolTip( _("Home page") );
- bSizer166->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer251->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
- m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("https://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync.org"), wxT("https://freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink1->SetToolTip( _("https://www.freefilesync.org") );
+ m_hyperlink1->SetToolTip( _("https://freefilesync.org") );
+
+ bSizer251->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
- bSizer166->Add( m_hyperlink1, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer166->Add( bSizer251, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ wxBoxSizer* bSizer250;
+ bSizer250 = new wxBoxSizer( wxVERTICAL );
m_bitmapForum = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_bitmapForum->SetToolTip( _("FreeFileSync Forum") );
- bSizer166->Add( m_bitmapForum, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer250->Add( m_bitmapForum, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
- m_hyperlink21 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync Forum"), wxT("https://www.freefilesync.org/forum/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink21 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("FreeFileSync Forum"), wxT("https://freefilesync.org/forum/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink21->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
m_hyperlink21->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink21->SetToolTip( _("https://www.freefilesync.org/forum/") );
+ m_hyperlink21->SetToolTip( _("https://freefilesync.org/forum/") );
- bSizer166->Add( m_hyperlink21, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer250->Add( m_hyperlink21, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer166->Add( bSizer250, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ wxBoxSizer* bSizer249;
+ bSizer249 = new wxBoxSizer( wxVERTICAL );
m_bitmapEmail = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_bitmapEmail->SetToolTip( _("Email") );
- bSizer166->Add( m_bitmapEmail, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+ bSizer249->Add( m_bitmapEmail, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
m_hyperlink2 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("zenju@freefilesync.org"), wxT("mailto:zenju@freefilesync.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink2->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, true, wxEmptyString ) );
m_hyperlink2->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
m_hyperlink2->SetToolTip( _("mailto:zenju@freefilesync.org") );
- bSizer166->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer249->Add( m_hyperlink2, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 );
- bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
+ bSizer166->Add( bSizer249, 0, wxALIGN_BOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer186->Add( bSizer166, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+ bSizer186->Add( bSizer166, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
- bSizer162->Add( bSizer186, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
+ bSizerMainSection->Add( bSizer186, 0, wxALL|wxEXPAND, 5 );
m_staticline3412 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer162->Add( m_staticline3412, 0, wxEXPAND, 5 );
-
- wxBoxSizer* bSizer174;
- bSizer174 = new wxBoxSizer( wxHORIZONTAL );
-
- wxBoxSizer* bSizer181;
- bSizer181 = new wxBoxSizer( wxVERTICAL );
+ bSizerMainSection->Add( m_staticline3412, 0, wxEXPAND, 5 );
m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) );
@@ -4254,7 +4256,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Support with a donation"), wxDefaultPosition, wxDefaultSize, 0 );
m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
- m_buttonDonate->SetToolTip( _("https://www.freefilesync.org/donate.php") );
+ m_buttonDonate->SetToolTip( _("https://freefilesync.org/donate.php") );
bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
@@ -4274,7 +4276,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_panelDonate->SetSizer( bSizer183 );
m_panelDonate->Layout();
bSizer183->Fit( m_panelDonate );
- bSizer181->Add( m_panelDonate, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
+ bSizerMainSection->Add( m_panelDonate, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
m_panelThankYou = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelThankYou->SetBackgroundColour( wxColour( 153, 170, 187 ) );
@@ -4325,7 +4327,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_panelThankYou->SetSizer( bSizer1831 );
m_panelThankYou->Layout();
bSizer1831->Fit( m_panelThankYou );
- bSizer181->Add( m_panelThankYou, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
+ bSizerMainSection->Add( m_panelThankYou, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
wxBoxSizer* bSizer187;
bSizer187 = new wxBoxSizer( wxVERTICAL );
@@ -4379,9 +4381,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
- m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("http://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ m_hyperlink13 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Boost"), wxT("https://www.boost.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
m_hyperlink13->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- m_hyperlink13->SetToolTip( _("http://www.boost.org") );
+ m_hyperlink13->SetToolTip( _("https://www.boost.org") );
bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
@@ -4413,10 +4415,10 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer187->Add( bSizer172, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
- bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 );
+ bSizerMainSection->Add( bSizer187, 0, wxALL|wxEXPAND, 5 );
m_staticline34 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer181->Add( m_staticline34, 0, wxEXPAND, 5 );
+ bSizerMainSection->Add( m_staticline34, 0, wxEXPAND, 5 );
wxBoxSizer* bSizer185;
bSizer185 = new wxBoxSizer( wxVERTICAL );
@@ -4440,10 +4442,10 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer185->Add( bSizer1671, 0, wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer181->Add( bSizer185, 0, wxALL|wxEXPAND, 5 );
+ bSizerMainSection->Add( bSizer185, 0, wxALL|wxEXPAND, 5 );
- bSizer174->Add( bSizer181, 0, 0, 5 );
+ bSizer174->Add( bSizerMainSection, 0, 0, 5 );
m_staticline37 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer174->Add( m_staticline37, 0, wxEXPAND, 5 );
@@ -4451,11 +4453,14 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer177;
bSizer177 = new wxBoxSizer( wxVERTICAL );
+ m_staticline74 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer177->Add( m_staticline74, 0, wxEXPAND, 5 );
+
m_staticTextThanksForLoc = new wxStaticText( m_panel41, wxID_ANY, _("Many thanks for localization:"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_staticTextThanksForLoc->Wrap( -1 );
m_staticTextThanksForLoc->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
- bSizer177->Add( m_staticTextThanksForLoc, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
+ bSizer177->Add( m_staticTextThanksForLoc, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 10 );
bSizer177->Add( 0, 5, 0, 0, 5 );
@@ -4472,18 +4477,15 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
m_scrolledWindowTranslators->SetSizer( fgSizerTranslators );
m_scrolledWindowTranslators->Layout();
fgSizerTranslators->Fit( m_scrolledWindowTranslators );
- bSizer177->Add( m_scrolledWindowTranslators, 1, wxLEFT|wxEXPAND, 5 );
-
-
- bSizer174->Add( bSizer177, 0, wxEXPAND|wxLEFT, 5 );
+ bSizer177->Add( m_scrolledWindowTranslators, 1, wxLEFT|wxEXPAND, 10 );
- bSizer162->Add( bSizer174, 0, 0, 5 );
+ bSizer174->Add( bSizer177, 0, wxEXPAND, 5 );
- m_panel41->SetSizer( bSizer162 );
+ m_panel41->SetSizer( bSizer174 );
m_panel41->Layout();
- bSizer162->Fit( m_panel41 );
+ bSizer174->Fit( m_panel41 );
bSizer31->Add( m_panel41, 0, wxEXPAND, 5 );
m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index 4c91d1fe..2358df75 100755
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jan 23 2018)
+// C++ code generated with wxFormBuilder (version May 29 2018)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -1036,6 +1036,7 @@ private:
protected:
wxPanel* m_panel41;
+ wxBoxSizer* bSizerMainSection;
wxStaticBitmap* m_bitmapLogo;
wxStaticLine* m_staticline341;
wxStaticText* m_staticText94;
@@ -1074,6 +1075,7 @@ protected:
wxStaticBitmap* m_bitmapGpl;
wxHyperlinkCtrl* m_hyperlink5;
wxStaticLine* m_staticline37;
+ wxStaticLine* m_staticline74;
wxStaticText* m_staticTextThanksForLoc;
wxScrolledWindow* m_scrolledWindowTranslators;
wxFlexGridSizer* fgSizerTranslators;
@@ -1163,7 +1165,7 @@ protected:
public:
- ActivationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync Donation Edition"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+ ActivationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~ActivationDlgGenerated();
};
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index 198b5726..312ee7db 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -12,9 +12,9 @@
#include <wx+/bitmap_button.h>
#include <wx+/popup_dlg.h>
#include "main_dlg.h"
-#include "../lib/generate_logfile.h"
-#include "../lib/resolve_path.h"
-#include "../lib/status_handler_impl.h"
+#include "../base/generate_logfile.h"
+#include "../base/resolve_path.h"
+#include "../base/status_handler_impl.h"
using namespace zen;
using namespace fff;
@@ -343,7 +343,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
if (!commandLine.empty())
try
{
- //use ExecutionType::ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31
+ //use ExecutionType::ASYNC until there is reason not to: https://freefilesync.org/forum/viewtopic.php?t=31
shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError
}
catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); }
diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h
index ae8125f6..48dbbd29 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.h
+++ b/FreeFileSync/Source/ui/gui_status_handler.h
@@ -11,7 +11,7 @@
#include <wx/event.h>
#include "progress_indicator.h"
#include "main_dlg.h"
-#include "../lib/status_handler.h"
+#include "../base/status_handler.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 5e40c4c4..7925034c 100755
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -36,15 +36,15 @@
#include "batch_config.h"
#include "triple_splitter.h"
#include "app_icon.h"
-#include "../comparison.h"
-#include "../synchronization.h"
-#include "../algorithm.h"
+#include "../base/comparison.h"
+#include "../base/synchronization.h"
+#include "../base/algorithm.h"
#include "../fs/concrete.h"
-#include "../lib/resolve_path.h"
-#include "../lib/ffs_paths.h"
-#include "../lib/help_provider.h"
-#include "../lib/lock_holder.h"
-#include "../lib/localization.h"
+#include "../base/resolve_path.h"
+#include "../base/ffs_paths.h"
+#include "../base/help_provider.h"
+#include "../base/lock_holder.h"
+#include "../base/localization.h"
#include "../version/version.h"
using namespace zen;
@@ -298,14 +298,14 @@ void MainDialog::create(const Zstring& globalConfigFilePath)
//------------------------------------------------------------------------------------------
//check existence of all files in parallel:
- GetFirstResult<FalseType> firstUnavailableFile;
+ GetFirstResult<std::false_type> firstUnavailableFile;
for (const Zstring& filePath : cfgFilePaths)
- firstUnavailableFile.addJob([filePath]() -> Opt<FalseType>
+ firstUnavailableFile.addJob([filePath]() -> Opt<std::false_type>
{
assert(!filePath.empty());
if (!fileAvailable(filePath))
- return FalseType();
+ return std::false_type();
return NoValue();
});
@@ -534,10 +534,10 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
m_gridOverview->Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(MainDialog::onTreeGridSelection), nullptr, this);
//cfg grid:
- m_gridCfgHistory->Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(MainDialog::onCfgGridSelection), nullptr, this);
- m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onCfgGridDoubleClick), nullptr, this);
- m_gridCfgHistory->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onCfgGridKeyEvent), nullptr, this);
- m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onCfgGridContext), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(MainDialog::onCfgGridSelection), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onCfgGridDoubleClick), nullptr, this);
+ m_gridCfgHistory->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onCfgGridKeyEvent), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onCfgGridContext), nullptr, this);
m_gridCfgHistory->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onCfgGridLabelContext ), nullptr, this);
m_gridCfgHistory->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(MainDialog::onCfgGridLabelLeftClick), nullptr, this);
//----------------------------------------------------------------------------------
@@ -752,9 +752,9 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check!
{
//check existence of all directories in parallel!
- GetFirstResult<FalseType> firstMissingDir;
+ GetFirstResult<std::false_type> firstMissingDir;
for (const AbstractPath& folderPath : folderPathsToCheck)
- firstMissingDir.addJob([folderPath]() -> Opt<FalseType>
+ firstMissingDir.addJob([folderPath]() -> Opt<std::false_type>
{
try
{
@@ -762,7 +762,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
return NoValue();
}
catch (FileError&) {}
- return FalseType();
+ return std::false_type();
});
const bool startComparisonNow = !firstMissingDir.timedWait(std::chrono::milliseconds(500)) || //= no result yet => start comparison anyway!
@@ -1088,17 +1088,12 @@ void MainDialog::setFilterManually(const std::vector<FileSystemObject*>& selecti
}
-namespace
-{
-//perf: wxString doesn't model exponential growth and is unsuitable for large data sets
-using zxString = Zbase<wchar_t>; //guaranteed exponential growth
-}
-
void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRefs)
{
try
{
- zxString clipboardString;
+//perf: wxString doesn't model exponential growth and is unsuitable for large data sets
+ Zstringw clipboardString;
auto addSelection = [&](const Grid& grid)
{
@@ -1112,10 +1107,10 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe
std::for_each(colAttr.begin(), colAttr.end() - 1,
[&](const Grid::ColAttributes& ca)
{
- clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type));
+ clipboardString += copyStringTo<Zstringw>(prov->getValue(row, ca.type));
clipboardString += L'\t';
});
- clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type));
+ clipboardString += copyStringTo<Zstringw>(prov->getValue(row, colAttr.back().type));
clipboardString += L'\n';
}
}
@@ -1332,7 +1327,7 @@ void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError
const std::vector<FileSystemObject*>& selection,
const TempFileBuffer& tempFileBuf)
{
- static const SelectedSide side2 = OtherSide<side>::result;
+ constexpr SelectedSide side2 = OtherSide<side>::value;
for (const FileSystemObject* fsObj : selection) //context menu calls this function only if selection is not empty!
{
@@ -2695,7 +2690,7 @@ void MainDialog::cfgHistoryRemoveObsolete(const std::vector<Zstring>& filePaths)
availableFiles.push_back(runAsync([=] { return fileAvailable(filePath); }));
//potentially slow network access => limit maximum wait time!
- wait_for_all_timed(availableFiles.begin(), availableFiles.end(), std::chrono::milliseconds(1000));
+ wait_for_all_timed(availableFiles.begin(), availableFiles.end(), std::chrono::seconds(1));
std::vector<Zstring> pathsToRemove;
@@ -2746,10 +2741,17 @@ void MainDialog::updateUnsavedCfgStatus()
else if (activeConfigFiles_.size() > 1)
{
title += extractJobName(activeConfigFiles_[0]);
- std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filepath) { title += SPACED_DASH + extractJobName(filepath); });
+ std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filePath) { title += SPACED_DASH + extractJobName(filePath); });
}
else
- title += wxString(L"FreeFileSync ") + ffsVersion + SPACED_DASH + _("Folder Comparison and Synchronization");
+ {
+ const std::wstring versionName = [&]
+ {
+ return L"FreeFileSync " + utfTo<std::wstring>(ffsVersion);
+ }();
+
+ title += versionName + SPACED_DASH + _("Folder Comparison and Synchronization");
+ }
SetTitle(title);
}
@@ -3725,7 +3727,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
{
flashStatusInformation(_("All files are in sync"));
- //update last sync date for selected cfg files https://www.freefilesync.org/forum/viewtopic.php?t=4991
+ //update last sync date for selected cfg files https://freefilesync.org/forum/viewtopic.php?t=4991
updateLastSyncTimesToNow();
}
}
@@ -3743,7 +3745,7 @@ void MainDialog::updateGui()
updateTopButton(*m_buttonSync, getResourceImage(L"file_sync"), getSyncVariantName(getConfig().mainCfg), folderCmp_.empty());
m_panelTopButtons->Layout();
- m_menuItemExportList->Enable(!folderCmp_.empty()); //a CSV without even folder names confuses users: https://www.freefilesync.org/forum/viewtopic.php?t=4787
+ m_menuItemExportList->Enable(!folderCmp_.empty()); //a CSV without even folder names confuses users: https://freefilesync.org/forum/viewtopic.php?t=4787
auiMgr_.Update(); //fix small display distortion, if view filter panel is empty
}
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index e4f4be05..3ca17d97 100755
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -8,7 +8,6 @@
#define MAIN_DLG_H_8910481324545644545
#include <map>
-#include <list>
#include <memory>
#include <wx+/async_task.h>
#include <wx+/file_drop.h>
@@ -18,7 +17,7 @@
#include "tree_grid.h"
#include "sync_cfg.h"
#include "folder_history_box.h"
-#include "../algorithm.h"
+#include "../base/algorithm.h"
namespace fff
@@ -312,7 +311,7 @@ private:
//***********************************************
//status information
- std::list<wxString> oldStatusMsgs_; //the first one is the original/non-flash status message
+ std::vector<wxString> oldStatusMsgs_; //the first one is the original/non-flash status message
//compare status panel (hidden on start, shown when comparing)
std::unique_ptr<CompareProgressDialog> compareStatus_; //always bound
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index dcb9539c..d3d5cf31 100755
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -31,8 +31,8 @@
#include <wx+/choice_enum.h>
#include <wx+/focus.h>
#include "gui_generated.h"
-#include "../lib/ffs_paths.h"
-#include "../lib/perf_check.h"
+#include "../base/ffs_paths.h"
+#include "../base/perf_check.h"
#include "tray_icon.h"
#include "taskbar.h"
#include "app_icon.h"
@@ -68,7 +68,7 @@ inline wxColor getColorItemsBackgroundRim() { return { 53, 25, 255 }; } //dark
//don't use wxStopWatch for long-running measurements: internally it uses ::QueryPerformanceCounter() which can overflow after only a few days:
-//https://www.freefilesync.org/forum/viewtopic.php?t=1426
+//https://freefilesync.org/forum/viewtopic.php?t=1426
// std::chrono::system_clock is not a steady clock, but at least doesn't overflow! (wraps ::GetSystemTimePreciseAsFileTime())
// std::chrono::steady_clock also wraps ::QueryPerformanceCounter() => same flaw like wxStopWatch???
@@ -519,7 +519,7 @@ public:
{
time_t time = 0;
MessageType type = MSG_TYPE_INFO;
- MsgString messageLine;
+ Zstringw messageLine;
bool firstLine = false; //if LogEntry::message spans multiple rows
};
@@ -546,7 +546,7 @@ public:
for (auto it = log_.begin(); it != log_.end(); ++it)
if (it->type & includedTypes)
{
- static_assert(IsSameType<GetCharType<MsgString>::Type, wchar_t>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<Zstringw>, wchar_t>);
assert(!startsWith(it->message, L'\n'));
size_t rowNumber = 0;
@@ -568,19 +568,19 @@ public:
}
private:
- static MsgString extractLine(const MsgString& message, size_t textRow)
+ static Zstringw extractLine(const Zstringw& message, size_t textRow)
{
auto it1 = message.begin();
for (;;)
{
auto it2 = std::find_if(it1, message.end(), [](wchar_t c) { return c == L'\n'; });
if (textRow == 0)
- return it1 == message.end() ? MsgString() : MsgString(&*it1, it2 - it1); //must not dereference iterator pointing to "end"!
+ return it1 == message.end() ? Zstringw() : Zstringw(&*it1, it2 - it1); //must not dereference iterator pointing to "end"!
if (it2 == message.end())
{
assert(false);
- return MsgString();
+ return Zstringw();
}
it1 = it2 + 1; //skip newline
@@ -824,8 +824,7 @@ private:
{
if (auto* prov = dynamic_cast<GridDataMessages*>(m_gridMessages->getDataProvider()))
return prov->getDataView();
-
- throw std::runtime_error("m_gridMessages was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] m_gridMessages was not initialized.");
}
void OnErrors(wxCommandEvent& event) override
@@ -972,8 +971,7 @@ private:
{
try
{
- using zxString = Zbase<wchar_t>; //guaranteed exponential growth, unlike wxString
- zxString clipboardString;
+ Zstringw clipboardString; //guaranteed exponential growth, unlike wxString
if (auto prov = m_gridMessages->getDataProvider())
{
@@ -985,10 +983,10 @@ private:
std::for_each(colAttr.begin(), --colAttr.end(),
[&](const Grid::ColAttributes& ca)
{
- clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type));
+ clipboardString += copyStringTo<Zstringw>(prov->getValue(row, ca.type));
clipboardString += L'\t';
});
- clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type));
+ clipboardString += copyStringTo<Zstringw>(prov->getValue(row, colAttr.back().type));
clipboardString += L'\n';
}
}
@@ -1372,9 +1370,9 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF
syncStat_ (&syncStat),
abortCb_ (&abortCb)
{
- static_assert(IsSameType<TopLevelDialog, wxFrame >::value ||
- IsSameType<TopLevelDialog, wxDialog>::value, "");
- assert((IsSameType<TopLevelDialog, wxFrame>::value == !parentFrame));
+ static_assert(std::is_same_v<TopLevelDialog, wxFrame > ||
+ std::is_same_v<TopLevelDialog, wxDialog>);
+ assert((std::is_same_v<TopLevelDialog, wxFrame> == !parentFrame));
//finish construction of this dialog:
this->pnl_.m_panelProgress->SetMinSize(wxSize(fastFromDIP(550), fastFromDIP(340)));
diff --git a/FreeFileSync/Source/ui/progress_indicator.h b/FreeFileSync/Source/ui/progress_indicator.h
index ca45bd72..0a4d0ed8 100755
--- a/FreeFileSync/Source/ui/progress_indicator.h
+++ b/FreeFileSync/Source/ui/progress_indicator.h
@@ -11,8 +11,8 @@
#include <zen/error_log.h>
#include <zen/zstring.h>
#include <wx/frame.h>
-#include "../lib/status_handler.h"
-#include "../lib/process_xml.h"
+#include "../base/status_handler.h"
+#include "../base/process_xml.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index 29365b27..f332c6e5 100755
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -24,12 +24,12 @@
#include "gui_generated.h"
#include "folder_selector.h"
#include "version_check.h"
-#include "../algorithm.h"
-#include "../synchronization.h"
-#include "../lib/help_provider.h"
-#include "../lib/hard_filter.h"
+#include "../base/algorithm.h"
+#include "../base/synchronization.h"
+#include "../base/help_provider.h"
+#include "../base/hard_filter.h"
+#include "../base/status_handler.h" //updateUiIsAllowed()
#include "../version/version.h"
-#include "../lib/status_handler.h" //updateUiIsAllowed()
@@ -46,7 +46,7 @@ public:
private:
void OnOK (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_OKAY); }
void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
- void OnDonate(wxCommandEvent& event) override { wxLaunchDefaultBrowser(L"https://www.freefilesync.org/donate.php"); }
+ void OnDonate(wxCommandEvent& event) override { wxLaunchDefaultBrowser(L"https://freefilesync.org/donate.php"); }
void onLocalKeyEvent(wxKeyEvent& event);
};
@@ -120,8 +120,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
wxImage versionImage = stackImages(appnameImg, buildImg, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER, 0);
const int borderSize = fastFromDIP(5);
-
- wxBitmap headerBmp(GetClientSize().GetWidth(), versionImage.GetHeight() + 2 * borderSize, 24);
+ wxBitmap headerBmp(bSizerMainSection->GetSize().x, versionImage.GetHeight() + 2 * borderSize, 24);
//attention: *must* pass 24 bits, auto-determination fails on Windows high-contrast colors schemes!!!
//problem only shows when calling wxDC::DrawBitmap
{
@@ -585,7 +584,6 @@ OptionsDlg::OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalSettings) :
m_bitmapSettings ->SetBitmap (getResourceImage(L"settings"));
m_bpButtonAddRow ->SetBitmapLabel(getResourceImage(L"item_add"));
m_bpButtonRemoveRow->SetBitmapLabel(getResourceImage(L"item_remove"));
- setBitmapTextLabel(*m_buttonResetDialogs, getResourceImage(L"reset_dialogs").ConvertToImage(), m_buttonResetDialogs->GetLabel());
m_staticTextResetDialogs->Wrap(std::max(fastFromDIP(200), m_buttonResetDialogs->GetMinSize().x));
@@ -647,9 +645,13 @@ void OptionsDlg::onResize(wxSizeEvent& event)
void OptionsDlg::updateGui()
{
- m_buttonResetDialogs->Enable(confirmDlgs_ != defaultCfg_.confirmDlgs ||
- warnDlgs_ != defaultCfg_.warnDlgs ||
- autoCloseProgressDialog_ != defaultCfg_.autoCloseProgressDialog);
+ const bool haveHiddenDialogs = confirmDlgs_ != defaultCfg_.confirmDlgs ||
+ warnDlgs_ != defaultCfg_.warnDlgs ||
+ autoCloseProgressDialog_ != defaultCfg_.autoCloseProgressDialog;
+
+ setBitmapTextLabel(*m_buttonResetDialogs, getResourceImage(L"reset_dialogs").ConvertToImage(), haveHiddenDialogs ? _("Show hidden dialogs again") : _("No hidden dialogs"));
+ Layout();
+ m_buttonResetDialogs->Enable(haveHiddenDialogs);
}
@@ -963,6 +965,8 @@ ActivationDlg::ActivationDlg(wxWindow* parent,
{
setStandardButtonLayout(*bSizerStdButtons, StdButtons().setCancel(m_buttonCancel));
+ SetTitle(std::wstring(L"FreeFileSync ") + ffsVersion + L" [" + _("Donation Edition") + L"]");
+
//setMainInstructionFont(*m_staticTextMain);
m_bitmapActivation->SetBitmap(getResourceImage(L"website"));
diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h
index 6d79b416..c5b806ce 100755
--- a/FreeFileSync/Source/ui/small_dlgs.h
+++ b/FreeFileSync/Source/ui/small_dlgs.h
@@ -8,8 +8,8 @@
#define SMALL_DLGS_H_8321790875018750245
#include <wx/window.h>
-#include "../lib/process_xml.h"
-#include "../synchronization.h"
+#include "../base/process_xml.h"
+#include "../base/synchronization.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h
index 8fdeca5b..8c63be1c 100755
--- a/FreeFileSync/Source/ui/sorting.h
+++ b/FreeFileSync/Source/ui/sorting.h
@@ -7,8 +7,8 @@
#ifndef SORTING_H_82574232452345
#define SORTING_H_82574232452345
-#include <zen/type_tools.h>
-#include "../file_hierarchy.h"
+#include <zen/type_traits.h>
+#include "../base/file_hierarchy.h"
namespace fff
@@ -52,7 +52,7 @@ bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b)
return true;
//sort directories and files/symlinks by short name
- return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>());
+ return zen::makeSortDirection(LessNaturalSort() /*even on Linux*/, std::bool_constant<ascending>())(a.getItemName<side>(), b.getItemName<side>());
}
@@ -65,7 +65,7 @@ bool lessFullPath(const FileSystemObject& a, const FileSystemObject& b)
else if (b.isEmpty<side>())
return true;
- return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())(
+ return zen::makeSortDirection(LessNaturalSort() /*even on Linux*/, std::bool_constant<ascending>())(
zen::utfTo<Zstring>(AFS::getDisplayPath(a.getAbstractPath<side>())),
zen::utfTo<Zstring>(AFS::getDisplayPath(b.getAbstractPath<side>())));
}
@@ -88,7 +88,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b)
const int rv = CmpNaturalSort()(relFolderA.c_str(), relFolderA.size(),
relFolderB.c_str(), relFolderB.size());
if (rv != 0)
- return makeSortDirection(std::less<int>(), zen::Int2Type<ascending>())(rv, 0);
+ return zen::makeSortDirection(std::less<int>(), std::bool_constant<ascending>())(rv, 0);
//make directories always appear before contained files
if (isDirectoryB)
@@ -96,7 +96,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b)
else if (isDirectoryA)
return true;
- return makeSortDirection(LessNaturalSort(), zen::Int2Type<ascending>())(a.getPairItemName(), b.getPairItemName());
+ return zen::makeSortDirection(LessNaturalSort(), std::bool_constant<ascending>())(a.getPairItemName(), b.getPairItemName());
}
@@ -125,7 +125,7 @@ bool lessFilesize(const FileSystemObject& a, const FileSystemObject& b)
return true;
//return list beginning with largest files first
- return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(fileA->getFileSize<side>(), fileB->getFileSize<side>());
+ return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(fileA->getFileSize<side>(), fileB->getFileSize<side>());
}
@@ -152,7 +152,7 @@ bool lessFiletime(const FileSystemObject& a, const FileSystemObject& b)
const int64_t dateB = fileB ? fileB->getLastWriteTime<side>() : symlinkB->getLastWriteTime<side>();
//return list beginning with newest files first
- return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(dateA, dateB);
+ return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(dateA, dateB);
}
@@ -174,7 +174,7 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b)
return afterLast(fsObj.getItemName<side>(), Zchar('.'), zen::IF_MISSING_RETURN_NONE);
};
- return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())(getExtension(a), getExtension(b));
+ return zen::makeSortDirection(LessNaturalSort() /*even on Linux*/, std::bool_constant<ascending>())(getExtension(a), getExtension(b));
}
@@ -187,14 +187,14 @@ bool lessCmpResult(const FileSystemObject& a, const FileSystemObject& b)
if (b.getCategory() == FILE_EQUAL)
return true;
- return makeSortDirection(std::less<CompareFilesResult>(), zen::Int2Type<ascending>())(a.getCategory(), b.getCategory());
+ return zen::makeSortDirection(std::less<CompareFilesResult>(), std::bool_constant<ascending>())(a.getCategory(), b.getCategory());
}
template <bool ascending> inline
bool lessSyncDirection(const FileSystemObject& a, const FileSystemObject& b)
{
- return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(a.getSyncOperation(), b.getSyncOperation());
+ return zen::makeSortDirection(std::less<>(), std::bool_constant<ascending>())(a.getSyncOperation(), b.getSyncOperation());
}
}
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index 101e2b8b..a1802988 100755
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -19,9 +19,9 @@
#include "gui_generated.h"
#include "command_box.h"
#include "folder_selector.h"
-#include "../file_hierarchy.h"
-#include "../lib/help_provider.h"
-#include "../lib/norm_filter.h"
+#include "../base/file_hierarchy.h"
+#include "../base/help_provider.h"
+#include "../base/norm_filter.h"
#include "../fs/concrete.h"
@@ -68,7 +68,6 @@ private:
void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"comparison-settings", this); }
void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"daylight-saving-time", this); }
void OnHelpPerformance (wxHyperlinkEvent& event) override { displayHelpEntry(L"performance", this); }
- warn_static("write performance help entry")
void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ }
void OnCompByTimeSize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::TIME_SIZE; updateCompGui(); updateSyncGui(); } //
diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h
index fd41a529..bf64fcb4 100755
--- a/FreeFileSync/Source/ui/sync_cfg.h
+++ b/FreeFileSync/Source/ui/sync_cfg.h
@@ -8,7 +8,7 @@
#define SYNC_CFG_H_31289470134253425
#include <wx/window.h>
-#include "../structures.h"
+#include "../base/structures.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp
index 66125d61..c4bc7bec 100755
--- a/FreeFileSync/Source/ui/taskbar.cpp
+++ b/FreeFileSync/Source/ui/taskbar.cpp
@@ -25,10 +25,10 @@ class Taskbar::Impl //throw (TaskbarNotAvailable)
{
public:
Impl(const wxFrame& window) :
- tbEntry(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE))
- //tbEntry(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop"))
+ tbEntry_(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE))
+ //tbEntry_(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop"))
{
- if (!tbEntry)
+ if (!tbEntry_)
throw TaskbarNotAvailable();
}
@@ -39,32 +39,32 @@ public:
switch (status)
{
case Taskbar::STATUS_ERROR:
- unity_launcher_entry_set_urgent(tbEntry, true);
+ unity_launcher_entry_set_urgent(tbEntry_, true);
break;
case Taskbar::STATUS_INDETERMINATE:
- unity_launcher_entry_set_urgent(tbEntry, false);
- unity_launcher_entry_set_progress_visible(tbEntry, false);
+ unity_launcher_entry_set_urgent(tbEntry_, false);
+ unity_launcher_entry_set_progress_visible(tbEntry_, false);
break;
case Taskbar::STATUS_NORMAL:
- unity_launcher_entry_set_urgent(tbEntry, false);
- unity_launcher_entry_set_progress_visible(tbEntry, true);
+ unity_launcher_entry_set_urgent(tbEntry_, false);
+ unity_launcher_entry_set_progress_visible(tbEntry_, true);
break;
case Taskbar::STATUS_PAUSED:
- unity_launcher_entry_set_urgent(tbEntry, false);
+ unity_launcher_entry_set_urgent(tbEntry_, false);
break;
}
}
void setProgress(double fraction)
{
- unity_launcher_entry_set_progress(tbEntry, fraction);
+ unity_launcher_entry_set_progress(tbEntry_, fraction);
}
private:
- UnityLauncherEntry* tbEntry;
+ UnityLauncherEntry* const tbEntry_;
};
#else //no taskbar support
diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp
index de7561d0..83a733df 100755
--- a/FreeFileSync/Source/ui/tree_grid.cpp
+++ b/FreeFileSync/Source/ui/tree_grid.cpp
@@ -16,7 +16,7 @@
#include <wx+/dc.h>
#include <wx+/context_menu.h>
#include <wx+/image_resources.h>
-#include "../lib/icon_buffer.h"
+#include "../base/icon_buffer.h"
using namespace zen;
using namespace fff;
@@ -69,7 +69,7 @@ void TreeView::extractVisibleSubtree(ContainerObject& hierObj, //in
// }
//prefer file-browser semantics over sync preview (=> always show useful numbers, even for SyncDirection::NONE)
- //discussion: https://www.freefilesync.org/forum/viewtopic.php?t=1595
+ //discussion: https://freefilesync.org/forum/viewtopic.php?t=1595
return std::max(file.getFileSize<LEFT_SIDE>(), file.getFileSize<RIGHT_SIDE>());
};
@@ -180,8 +180,8 @@ struct TreeView::LessShortName
{
case TreeView::TYPE_ROOT:
return makeSortDirection(LessNaturalSort() /*even on Linux*/,
- Int2Type<ascending>())(utfTo<Zstring>(static_cast<const RootNodeImpl*>(lhs.node)->displayName),
- utfTo<Zstring>(static_cast<const RootNodeImpl*>(rhs.node)->displayName));
+ std::bool_constant<ascending>())(utfTo<Zstring>(static_cast<const RootNodeImpl*>(lhs.node)->displayName),
+ utfTo<Zstring>(static_cast<const RootNodeImpl*>(rhs.node)->displayName));
case TreeView::TYPE_DIRECTORY:
{
@@ -193,7 +193,7 @@ struct TreeView::LessShortName
else if (!folderR)
return true;
- return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(folderL->getPairItemName(), folderR->getPairItemName());
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, std::bool_constant<ascending>())(folderL->getPairItemName(), folderR->getPairItemName());
}
case TreeView::TYPE_FILES:
@@ -246,10 +246,10 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeTree colu
std::sort(items.begin(), items.end(), LessShortName<ascending>());
break;
case ColumnTypeTree::ITEM_COUNT:
- std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type<ascending>()));
+ std::sort(items.begin(), items.end(), makeSortDirection(lessCount, std::bool_constant<ascending>()));
break;
case ColumnTypeTree::BYTES:
- std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type<ascending>()));
+ std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, std::bool_constant<ascending>()));
break;
}
}
@@ -840,7 +840,6 @@ private:
//percentage bar
if (showPercentBar_)
{
-
const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, percentageBarWidth_, rectTmp.height - 4);
{
//clear background
@@ -1207,7 +1206,8 @@ void treegrid::init(Grid& grid)
grid.setDataProvider(std::make_shared<GridDataTree>(grid));
grid.showRowLabel(false);
- const int rowHeight = std::max(IconBuffer::getSize(IconBuffer::SIZE_SMALL), grid.getMainWin().GetCharHeight()) + 2; //allow 1 pixel space on top and bottom; dearly needed on OS X!
+ const int rowHeight = std::max(IconBuffer::getSize(IconBuffer::SIZE_SMALL) + 2, //1 extra pixel on top/bottom; dearly needed on OS X!
+ grid.getMainWin().GetCharHeight()); //seems to already include 3 margin pixels on top/bottom (consider percentage area)
grid.setRowHeight(rowHeight);
}
@@ -1216,8 +1216,7 @@ TreeView& treegrid::getDataView(Grid& grid)
{
if (auto* prov = dynamic_cast<GridDataTree*>(grid.getDataProvider()))
return prov->getDataView();
-
- throw std::runtime_error("treegrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] treegrid was not initialized.");
}
diff --git a/FreeFileSync/Source/ui/tree_grid.h b/FreeFileSync/Source/ui/tree_grid.h
index 22ae293d..6bbfb446 100755
--- a/FreeFileSync/Source/ui/tree_grid.h
+++ b/FreeFileSync/Source/ui/tree_grid.h
@@ -11,7 +11,7 @@
#include <zen/optional.h>
#include <wx+/grid.h>
#include "tree_grid_attr.h"
-#include "../file_hierarchy.h"
+#include "../base/file_hierarchy.h"
namespace fff
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index 822ce230..47863f06 100755
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -12,11 +12,11 @@
#include <zen/build_info.h>
#include <zen/basic_math.h>
#include <zen/file_error.h>
-#include <zen/thread.h> //std::thread::id
+#include <zen/http.h>
+#include <zen/thread.h>
#include <wx+/popup_dlg.h>
-#include <wx+/http.h>
#include <wx+/image_resources.h>
-#include "../lib/ffs_paths.h"
+#include "../base/ffs_paths.h"
#include "small_dlgs.h"
#include "version_check_impl.h"
@@ -28,12 +28,11 @@ using namespace fff;
namespace
{
-
-const wchar_t ffsUpdateCheckUserAgent[] = L"FFS-Update-Check";
+const Zchar ffsUpdateCheckUserAgent[] = Zstr("FFS-Update-Check");
std::wstring getIso639Language()
{
- assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, consider wxWidgets usage
+ assert(runningMainThread()); //this function is not thread-safe, consider wxWidgets usage
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
if (!localeName.empty())
@@ -48,7 +47,7 @@ std::wstring getIso639Language()
std::wstring getIso3166Country()
{
- assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, consider wxWidgets usage
+ assert(runningMainThread()); //this function is not thread-safe, consider wxWidgets usage
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
if (!localeName.empty())
@@ -64,7 +63,7 @@ std::wstring getIso3166Country()
//coordinate with get_latest_version_number.php
std::vector<std::pair<std::string, std::string>> geHttpPostParameters()
{
- assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, e.g. consider wxWidgets usage in isPortableVersion()
+ assert(runningMainThread()); //this function is not thread-safe, e.g. consider wxWidgets usage in isPortableVersion()
std::vector<std::pair<std::string, std::string>> params;
params.emplace_back("ffs_version", ffsVersion);
@@ -107,7 +106,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio
try
{
//consider wxHTTP limitation: URL must be accessible without https!!!
- const std::string buf = sendHttpPost(L"http://www.freefilesync.org/get_latest_changes.php", ffsUpdateCheckUserAgent,
+ const std::string buf = sendHttpPost(Zstr("http://freefilesync.org/get_latest_changes.php"), ffsUpdateCheckUserAgent,
nullptr /*notifyUnbufferedIO*/, { { "since", ffsVersion } }).readAll(); //throw SysError
updateDetailsMsg = utfTo<std::wstring>(buf);
}
@@ -127,7 +126,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio
_("&Download")))
{
case ConfirmationButton::ACCEPT:
- wxLaunchDefaultBrowser(L"https://www.freefilesync.org/get_latest.php");
+ wxLaunchDefaultBrowser(L"https://freefilesync.org/get_latest.php");
break;
case ConfirmationButton::CANCEL:
break;
@@ -139,7 +138,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio
std::string getOnlineVersion(const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
{
//consider wxHTTP limitation: URL must be accessible without https!!!
- const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", ffsUpdateCheckUserAgent,
+ const std::string buffer = sendHttpPost(Zstr("http://freefilesync.org/get_latest_version_number.php"), ffsUpdateCheckUserAgent,
nullptr /*notifyUnbufferedIO*/, postParams).readAll(); //throw SysError
return trimCpy(buffer);
}
@@ -207,7 +206,7 @@ void fff::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion)
setDetailInstructions(e.toString()), _("&Check"), _("&Retry")))
{
case QuestionButton2::YES:
- wxLaunchDefaultBrowser(L"https://www.freefilesync.org/get_latest.php");
+ wxLaunchDefaultBrowser(L"https://freefilesync.org/get_latest.php");
break;
case QuestionButton2::NO: //retry
checkForUpdateNow(parent, lastOnlineVersion); //note: retry via recursion!!!
@@ -219,7 +218,7 @@ void fff::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion)
else
switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().
setTitle(_("Check for Program Updates")).
- setMainInstructions(replaceCpy(_("Unable to connect to %x."), L"%x", L"www.freefilesync.org")).
+ setMainInstructions(replaceCpy(_("Unable to connect to %x."), L"%x", L"freefilesync.org")).
setDetailInstructions(e.toString()), _("&Retry")))
{
case ConfirmationButton::ACCEPT: //retry
@@ -234,19 +233,18 @@ void fff::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion)
struct fff::UpdateCheckResultPrep
{
- const std::vector<std::pair<std::string, std::string>> postParameters { geHttpPostParameters() };
+ const std::vector<std::pair<std::string, std::string>> postParameters = geHttpPostParameters();
};
-//run on main thread:
std::shared_ptr<UpdateCheckResultPrep> fff::automaticUpdateCheckPrepare()
{
- return nullptr;
+ assert(runningMainThread());
+ return std::make_shared<UpdateCheckResultPrep>();
}
struct fff::UpdateCheckResult
{
- UpdateCheckResult() {}
UpdateCheckResult(const std::string& ver, const Opt<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {}
std::string onlineVersion;
@@ -254,28 +252,27 @@ struct fff::UpdateCheckResult
bool internetIsAlive = false;
};
-//run on worker thread:
std::shared_ptr<UpdateCheckResult> fff::automaticUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep)
{
- return nullptr;
-}
-
-
-//run on main thread:
-void fff::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* resultAsync)
-{
-
- UpdateCheckResult result;
+ //assert(!runningMainThread()); -> allow synchronous call, too
try
{
- result.onlineVersion = getOnlineVersion(geHttpPostParameters()); //throw SysError
- result.internetIsAlive = true;
+ const std::string onlineVersion = getOnlineVersion(resultPrep->postParameters); //throw SysError
+ return std::make_shared<UpdateCheckResult>(onlineVersion, NoValue(), true);
}
catch (const zen::SysError& e)
{
- result.error = e;
- result.internetIsAlive = internetIsAlive();
+ return std::make_shared<UpdateCheckResult>("", e, internetIsAlive());
}
+}
+
+
+void fff::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* asyncResult)
+{
+ assert(runningMainThread());
+
+
+ const UpdateCheckResult& result = *asyncResult;
if (!result.error)
{
@@ -298,10 +295,10 @@ void fff::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, st
_("&Check"), _("&Retry")))
{
case QuestionButton2::YES:
- wxLaunchDefaultBrowser(L"https://www.freefilesync.org/get_latest.php");
+ wxLaunchDefaultBrowser(L"https://freefilesync.org/get_latest.php");
break;
case QuestionButton2::NO: //retry
- automaticUpdateCheckEval(parent, lastUpdateCheck, lastOnlineVersion, resultAsync); //note: retry via recursion!!!
+ automaticUpdateCheckEval(parent, lastUpdateCheck, lastOnlineVersion, asyncResult); //note: retry via recursion!!!
break;
case QuestionButton2::CANCEL:
break;
diff --git a/FreeFileSync/Source/ui/version_check.h b/FreeFileSync/Source/ui/version_check.h
index 75cef84e..013849e4 100755
--- a/FreeFileSync/Source/ui/version_check.h
+++ b/FreeFileSync/Source/ui/version_check.h
@@ -30,7 +30,7 @@ std::shared_ptr<UpdateCheckResultPrep> automaticUpdateCheckPrepare();
std::shared_ptr<UpdateCheckResult> automaticUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep);
//run on main thread:
void automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion,
- const UpdateCheckResult* resultAsync);
+ const UpdateCheckResult* asyncResult);
//----------------------------------------------------------------------------
//call from main thread:
void checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion);
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 797c1901..158c2fc1 100755
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace fff
{
-const char ffsVersion[] = "10.0"; //internal linkage!
+const char ffsVersion[] = "10.1"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/wx+/async_task.h b/wx+/async_task.h
index 1d64e262..8c2602ca 100755
--- a/wx+/async_task.h
+++ b/wx+/async_task.h
@@ -49,12 +49,12 @@ public:
bool resultReady () const override { return isReady(asyncResult_); }
void evaluateResult() override
{
- evalResult(IsSameType<ResultType, void>());
+ evalResult(std::is_same<ResultType, void>());
}
private:
- void evalResult(FalseType /*void result type*/) { evalOnGui_(asyncResult_.get()); }
- void evalResult(TrueType /*void result type*/) { asyncResult_.get(); evalOnGui_(); }
+ void evalResult(std::false_type /*void result type*/) { evalOnGui_(asyncResult_.get()); }
+ void evalResult(std::true_type /*void result type*/) { asyncResult_.get(); evalOnGui_(); }
std::future<ResultType> asyncResult_;
Fun evalOnGui_; //keep "evalOnGui" strictly separated from async thread: in particular do not copy in thread!
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index a1beee01..adc0615c 100755
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -169,12 +169,12 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring&
if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
pt.x += rect.width - extentTrunc.GetWidth();
else if (alignment & wxALIGN_CENTER_HORIZONTAL)
- pt.x += (rect.width - extentTrunc.GetWidth()) / 2;
+ pt.x += static_cast<int>(std::floor((rect.width - extentTrunc.GetWidth()) / 2.0)); //round down negative values, too!
if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
pt.y += rect.height - extentTrunc.GetHeight();
else if (alignment & wxALIGN_CENTER_VERTICAL)
- pt.y += (rect.height - extentTrunc.GetHeight()) / 2;
+ pt.y += static_cast<int>(std::floor((rect.height - extentTrunc.GetHeight()) / 2.0)); //round down negative values, too!
RecursiveDcClipper clip(dc, rect);
dc.DrawText(textTrunc, pt);
diff --git a/wx+/http.cpp b/wx+/http.cpp
deleted file mode 100755
index 1526d30f..00000000
--- a/wx+/http.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "http.h"
-
- #include <wx/app.h>
- #include <zen/thread.h> //std::thread::id
- #include <wx/protocol/http.h>
-
-using namespace zen;
-
-
-namespace
-{
-
-struct UrlRedirectError
-{
- UrlRedirectError(const std::wstring& url) : newUrl(url) {}
- std::wstring newUrl;
-};
-}
-
-
-class HttpInputStream::Impl
-{
-public:
- Impl(const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError, UrlRedirectError
- const std::string* postParams) : //issue POST if bound, GET otherwise
- notifyUnbufferedIO_(notifyUnbufferedIO)
- {
- ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ );
-
- //assert(!startsWith(url, L"https:", CmpAsciiNoCase())); //not supported by wxHTTP!
-
- const std::wstring urlFmt = afterFirst(url, L"://", IF_MISSING_RETURN_NONE);
- const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
- const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
-
- assert(std::this_thread::get_id() == mainThreadId);
- assert(wxApp::IsMainLoopRunning());
-
- webAccess_.SetHeader(L"User-Agent", userAgent);
- webAccess_.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
-
- if (!webAccess_.Connect(server)) //will *not* fail for non-reachable url here!
- throw SysError(L"wxHTTP::Connect");
-
- if (postParams)
- if (!webAccess_.SetPostText(L"application/x-www-form-urlencoded", utfTo<wxString>(*postParams)))
- throw SysError(L"wxHTTP::SetPostText");
-
- httpStream_.reset(webAccess_.GetInputStream(page)); //pass ownership
- const int sc = webAccess_.GetResponse();
-
- //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
- if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
- {
- const std::wstring newUrl(webAccess_.GetHeader(L"Location"));
- if (newUrl.empty())
- throw SysError(L"Unresolvable redirect. Empty target Location.");
-
- throw UrlRedirectError(newUrl);
- }
-
- if (sc != 200) //HTTP_STATUS_OK
- throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
-
- if (!httpStream_ || webAccess_.GetError() != wxPROTO_NOERR)
- throw SysError(L"wxHTTP::GetError (" + numberTo<std::wstring>(webAccess_.GetError()) + L")");
- }
-
- ~Impl() { cleanup(); }
-
- size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream!
- {
- const size_t blockSize = getBlockSize();
- assert(memBuf_.size() >= blockSize);
- assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size());
-
- char* it = static_cast<char*>(buffer);
- char* const itEnd = it + bytesToRead;
- for (;;)
- {
- const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_);
- std::memcpy(it, &memBuf_[0] + bufPos_, junkSize);
- bufPos_ += junkSize;
- it += junkSize;
-
- if (it == itEnd)
- break;
- //--------------------------------------------------------------------
- const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- bufPos_ = 0;
- bufPosEnd_ = bytesRead;
-
- if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
-
- if (bytesRead == 0) //end of file
- break;
- }
- return it - static_cast<char*>(buffer);
- }
-
- size_t getBlockSize() const { return 64 * 1024; }
-
-private:
- size_t tryRead(void* buffer, size_t bytesToRead) //throw SysError; may return short, only 0 means EOF!
- {
- if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
- assert(bytesToRead == getBlockSize());
-
- httpStream_->Read(buffer, bytesToRead);
-
- const wxStreamError ec = httpStream_->GetLastError();
- if (ec != wxSTREAM_NO_ERROR && ec != wxSTREAM_EOF)
- throw SysError(L"wxInputStream::GetLastError (" + numberTo<std::wstring>(httpStream_->GetLastError()) + L")");
-
- const size_t bytesRead = httpStream_->LastRead();
- //"if there are not enough bytes in the stream right now, LastRead() value will be
- // less than size but greater than 0. If it is 0, it means that EOF has been reached."
- assert(bytesRead > 0 || ec == wxSTREAM_EOF);
- if (bytesRead > bytesToRead) //better safe than sorry
- throw SysError(L"InternetReadFile: buffer overflow.");
-
- return bytesRead; //"zero indicates end of file"
- }
-
- Impl (const Impl&) = delete;
- Impl& operator=(const Impl&) = delete;
-
- void cleanup()
- {
- }
-
- wxHTTP webAccess_;
- std::unique_ptr<wxInputStream> httpStream_; //must be deleted BEFORE webAccess is closed
-
- const IOCallback notifyUnbufferedIO_; //throw X
-
- std::vector<char> memBuf_ = std::vector<char>(getBlockSize());
- size_t bufPos_ = 0; //buffered I/O; see file_io.cpp
- size_t bufPosEnd_ = 0; //
-};
-
-
-HttpInputStream::HttpInputStream(std::unique_ptr<Impl>&& pimpl) : pimpl_(std::move(pimpl)) {}
-
-HttpInputStream::~HttpInputStream() {}
-
-size_t HttpInputStream::read(void* buffer, size_t bytesToRead) { return pimpl_->read(buffer, bytesToRead); } //throw SysError, X; return "bytesToRead" bytes unless end of stream!
-
-size_t HttpInputStream::getBlockSize() const { return pimpl_->getBlockSize(); }
-
-std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl_); } //throw SysError, X;
-
-namespace
-{
-std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError
- const std::string* postParams) //issue POST if bound, GET otherwise
-{
- std::wstring urlRed = url;
- //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
- for (int redirects = 0; redirects < 6; ++redirects)
- try
- {
- return std::make_unique<HttpInputStream::Impl>(urlRed, userAgent, notifyUnbufferedIO, postParams); //throw SysError, UrlRedirectError
- }
- catch (const UrlRedirectError& e) { urlRed = e.newUrl; }
- throw SysError(L"Too many redirects.");
-}
-
-
-//encode into "application/x-www-form-urlencoded"
-std::string urlencode(const std::string& str)
-{
- std::string out;
- for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/master/ext/standard/url.c#L500
- if (c == ' ')
- out += '+';
- else if (('0' <= c && c <= '9') ||
- ('A' <= c && c <= 'Z') ||
- ('a' <= c && c <= 'z') ||
- c == '-' || c == '.' || c == '_') //note: "~" is encoded by PHP!
- out += c;
- else
- {
- const std::pair<char, char> hex = hexify(c);
-
- out += '%';
- out += hex.first;
- out += hex.second;
- }
- return out;
-}
-
-
-std::string urldecode(const std::string& str)
-{
- std::string out;
- for (size_t i = 0; i < str.size(); ++i)
- {
- const char c = str[i];
- if (c == '+')
- out += ' ';
- else if (c == '%' && str.size() - i >= 3 &&
- isHexDigit(str[i + 1]) &&
- isHexDigit(str[i + 2]))
- {
- out += unhexify(str[i + 1], str[i + 2]);
- i += 2;
- }
- else
- out += c;
- }
- return out;
-}
-}
-
-
-std::string zen::xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs)
-{
- std::string output;
- for (const auto& pair : paramPairs)
- output += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
- //encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
- if (!output.empty())
- output.pop_back();
- return output;
-}
-
-
-std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string& str)
-{
- std::vector<std::pair<std::string, std::string>> output;
-
- for (const std::string& nvPair : split(str, '&', SplitType::SKIP_EMPTY))
- output.emplace_back(urldecode(beforeFirst(nvPair, '=', IF_MISSING_RETURN_ALL)),
- urldecode(afterFirst (nvPair, '=', IF_MISSING_RETURN_NONE)));
- return output;
-}
-
-
-HttpInputStream zen::sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO,
- const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
-{
- const std::string encodedParams = xWwwFormUrlEncode(postParams);
- return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, &encodedParams); //throw SysError
-}
-
-
-HttpInputStream zen::sendHttpGet(const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO) //throw SysError
-{
- return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, nullptr); //throw SysError
-}
-
-
-bool zen::internetIsAlive() //noexcept
-{
- assert(std::this_thread::get_id() == mainThreadId);
-
- const wxString server = L"www.google.com";
- const wxString page = L"/";
-
- wxHTTP webAccess;
- webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
-
- if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
- return false;
-
- std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //call before checking wxHTTP::GetResponse()
- const int sc = webAccess.GetResponse();
- //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
- return sc / 100 == 2 || //e.g. 200
- sc / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive!
-}
diff --git a/wx+/image_holder.h b/wx+/image_holder.h
index 6804d5fc..f4bf3c8a 100755
--- a/wx+/image_holder.h
+++ b/wx+/image_holder.h
@@ -18,15 +18,15 @@ struct ImageHolder //prepare conversion to wxImage as much as possible while sta
{
ImageHolder() {}
- ImageHolder(int w, int h, bool withAlpha) : //init with allocated memory
+ ImageHolder(int w, int h, bool withAlpha) : //init with memory allocated
width_(w), height_(h),
rgb_(static_cast<unsigned char*>(::malloc(w * h * 3))),
alpha_(withAlpha ? static_cast<unsigned char*>(::malloc(w * h)) : nullptr) {}
- ImageHolder (ImageHolder&& tmp) = default; //
- ImageHolder& operator=(ImageHolder&& tmp) = default; //move semantics only!
- ImageHolder (const ImageHolder&) = delete; //
- ImageHolder& operator=(const ImageHolder&) = delete; //
+ ImageHolder (ImageHolder&&) noexcept = default; //
+ ImageHolder& operator=(ImageHolder&&) noexcept = default; //move semantics only!
+ ImageHolder (const ImageHolder&) = delete; //
+ ImageHolder& operator=(const ImageHolder&) = delete; //
explicit operator bool() const { return rgb_.get() != nullptr; }
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index e361515a..bbeeff8b 100755
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -26,8 +26,6 @@ using namespace zen;
namespace
{
-
-
ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const unsigned char* imageRgb, const unsigned char* imageAlpha, int hqScale)
{
assert(imageRgb && imageAlpha); //see convertToVanillaImage()
@@ -84,113 +82,41 @@ ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const u
}
-struct WorkItem
+auto getScalerTask(const wxString& name, const wxImage& img, int hqScale, Protected<std::vector<std::pair<std::wstring, ImageHolder>>>& result)
{
- std::wstring name; //don't trust wxString to be thread-safe like an int
- int width = 0;
- int height = 0;
- int dpiWidth = 0;
- int dpiHeight = 0;
- const unsigned char* rgb = nullptr;
- const unsigned char* alpha = nullptr;
-};
-
-
-class WorkLoad
-{
-public:
- void add(const WorkItem& wi) //context of main thread
- {
- assert(std::this_thread::get_id() == mainThreadId);
- {
- std::lock_guard<std::mutex> dummy(lockWork_);
- workLoad_.push_back(wi);
- }
- conditionNewWork_.notify_all();
- }
-
- void noMoreWork()
+ return [name = copyStringTo<std::wstring>(name), //don't trust wxString to be thread-safe like an int
+ width = img.GetWidth(),
+ height = img.GetHeight(),
+ dpiWidth = fastFromDIP(img.GetWidth()),
+ dpiHeight = fastFromDIP(img.GetHeight()), //don't call fastFromDIP() from worker thread (wxWidgets function!)
+ rgb = img.GetData(),
+ alpha = img.GetAlpha(),
+ hqScale, &result]
{
- assert(std::this_thread::get_id() == mainThreadId);
- {
- std::lock_guard<std::mutex> dummy(lockWork_);
- expectMoreWork_ = false;
- }
- conditionNewWork_.notify_all();
- }
-
- //context of worker thread, blocking:
- Opt<WorkItem> extractNext() //throw ThreadInterruption
- {
- assert(std::this_thread::get_id() != mainThreadId);
- std::unique_lock<std::mutex> dummy(lockWork_);
-
- interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty() || !expectMoreWork_; }); //throw ThreadInterruption
-
- if (!workLoad_.empty())
- {
- WorkItem wi = std::move(workLoad_. back()); //
- /**/ workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc)
- return std::move(wi); //
- }
- return NoValue();
- }
-
-private:
- std::mutex lockWork_;
- std::vector<WorkItem> workLoad_;
- std::condition_variable conditionNewWork_; //signal event: data for processing available
- bool expectMoreWork_ = true;
-};
+ ImageHolder ih = dpiScale(width, height,
+ dpiWidth, dpiHeight,
+ rgb, alpha, hqScale);
+ result.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r) { r.emplace_back(name, std::move(ih)); });
+ };
+}
class DpiParallelScaler
{
public:
- DpiParallelScaler(int hqScale)
- {
- assert(hqScale > 1);
- const int threadCount = std::max<int>(std::thread::hardware_concurrency(), 1); //hardware_concurrency() == 0 if "not computable or well defined"
-
- for (int i = 0; i < threadCount; ++i)
- worker_.push_back([hqScale, &workload = workload_, &result = result_]
- {
- setCurrentThreadName("xBRZ Scaler");
- while (Opt<WorkItem> wi = workload.extractNext()) //throw ThreadInterruption
- {
- ImageHolder ih = dpiScale(wi->width, wi->height,
- wi->dpiWidth, wi->dpiHeight,
- wi->rgb, wi->alpha, hqScale);
- result.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r) { r.emplace_back(wi->name, std::move(ih)); });
- }
- });
- }
+ DpiParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); }
- ~DpiParallelScaler()
- {
- for (InterruptibleThread& w : worker_)
- w.interrupt();
-
- for (InterruptibleThread& w : worker_)
- if (w.joinable())
- w.join();
- }
+ ~DpiParallelScaler() { threadGroup_ = zen::NoValue(); } //DpiParallelScaler must out-live threadGroup!!!
void add(const wxString& name, const wxImage& img)
{
imgKeeper_.push_back(img); //retain (ref-counted) wxImage so that the rgb/alpha pointers remain valid after passed to threads
- workload_.add({ copyStringTo<std::wstring>(name),
- img.GetWidth(), img.GetHeight(),
- fastFromDIP(img.GetWidth()), fastFromDIP(img.GetHeight()), //don't call fastFromDIP() from worker thread (wxWidgets function!)
- img.GetData(), img.GetAlpha() });
+ threadGroup_->run(getScalerTask(name, img, hqScale_, result_));
}
std::map<wxString, wxBitmap> waitAndGetResult()
{
- workload_.noMoreWork();
-
- for (InterruptibleThread& w : worker_)
- w.join();
+ threadGroup_->wait();
std::map<wxString, wxBitmap> output;
@@ -203,17 +129,20 @@ public:
wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); //pass ownership
img.SetAlpha(ih.releaseAlpha(), false /*static_data*/);
- output[item.first] = wxBitmap(img);
+ output.emplace(item.first, std::move(img));
}
});
return output;
}
private:
- std::vector<InterruptibleThread> worker_;
- WorkLoad workload_;
- Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_;
+ const int hqScale_;
std::vector<wxImage> imgKeeper_;
+ Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_;
+
+ using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>;
+ Opt<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") };
+ //hardware_concurrency() == 0 if "not computable or well defined"
};
@@ -222,12 +151,12 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim)
//work around wxWidgets bug:
//construct seekable input stream (zip-input stream is not seekable) for wxAnimation::Load()
//luckily this method call is very fast: below measurement precision!
- std::vector<char> data;
+ std::vector<std::byte> data;
data.reserve(10000);
int newValue = 0;
while ((newValue = zipInput.GetC()) != wxEOF)
- data.push_back(newValue);
+ data.push_back(static_cast<std::byte>(newValue));
wxMemoryInputStream seekAbleStream(&data.front(), data.size()); //stream does not take ownership of data
@@ -243,7 +172,7 @@ public:
static std::shared_ptr<GlobalBitmaps> instance()
{
static Global<GlobalBitmaps> inst(std::make_unique<GlobalBitmaps>());
- assert(std::this_thread::get_id() == mainThreadId); //wxWidgets is not thread-safe!
+ assert(runningMainThread()); //wxWidgets is not thread-safe!
return inst.get();
}
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index 26a5b37c..88a78b21 100755
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -83,7 +83,7 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
switch (align)
{
case ImageStackAlignment::CENTER:
- return (totalExtent - imageExtent) / 2;
+ return static_cast<int>(std::floor((totalExtent - imageExtent) / 2.0)); //consistency: round down negative values, too!
case ImageStackAlignment::LEFT:
return 0;
case ImageStackAlignment::RIGHT:
diff --git a/wx+/zlib_wrap.h b/wx+/zlib_wrap.h
index 7265331e..d3bb017b 100755
--- a/wx+/zlib_wrap.h
+++ b/wx+/zlib_wrap.h
@@ -52,8 +52,8 @@ BinContainer compress(const BinContainer& stream, int level) //throw ZlibInterna
//save uncompressed stream size for decompression
const uint64_t uncompressedSize = stream.size(); //use portable number type!
contOut.resize(sizeof(uncompressedSize));
- std::copy(reinterpret_cast<const char*>(&uncompressedSize),
- reinterpret_cast<const char*>(&uncompressedSize) + sizeof(uncompressedSize),
+ std::copy(reinterpret_cast<const std::byte*>(&uncompressedSize),
+ reinterpret_cast<const std::byte*>(&uncompressedSize) + sizeof(uncompressedSize),
&*contOut.begin());
const size_t bufferEstimate = impl::zlib_compressBound(stream.size()); //upper limit for buffer size, larger than input size!!!
@@ -85,7 +85,7 @@ BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError
throw ZlibInternalError();
std::copy(&*stream.begin(),
&*stream.begin() + sizeof(uncompressedSize),
- reinterpret_cast<char*>(&uncompressedSize));
+ reinterpret_cast<std::byte*>(&uncompressedSize));
//attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!!
//secondary bug: don't dereference iterator into empty container!
if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib!
diff --git a/xBRZ/src/xbrz.cpp b/xBRZ/src/xbrz.cpp
index 4d3ccd25..3cbd0d64 100755
--- a/xBRZ/src/xbrz.cpp
+++ b/xBRZ/src/xbrz.cpp
@@ -29,7 +29,7 @@ namespace
template <unsigned int M, unsigned int N> inline
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
{
- static_assert(0 < M && M < N && N <= 1000, "");
+ static_assert(0 < M && M < N && N <= 1000);
auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
@@ -42,7 +42,7 @@ uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color wi
template <unsigned int M, unsigned int N> inline
uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
{
- static_assert(0 < M && M < N && N <= 1000, "");
+ static_assert(0 < M && M < N && N <= 1000);
const unsigned int weightFront = getAlpha(pixFront) * M;
const unsigned int weightBack = getAlpha(pixBack) * (N - M);
@@ -473,7 +473,7 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
const int bufferSize = srcWidth;
unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
- static_assert(BLEND_NONE == 0, "");
+ static_assert(BLEND_NONE == 0);
//initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
//this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
@@ -1071,7 +1071,7 @@ if (factor == 1)
return;
}
- static_assert(SCALE_FACTOR_MAX == 6, "");
+ static_assert(SCALE_FACTOR_MAX == 6);
switch (colFmt)
{
case ColorFormat::RGB:
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 16f69bde..0d08f6a6 100755
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -13,6 +13,7 @@
#include <cmath>
#include <functional>
#include <cassert>
+#include "type_traits.h"
namespace numeric
@@ -65,6 +66,7 @@ const double sqrt2 = 1.41421356237309504880;
const double ln2 = 0.693147180559945309417;
//static_assert(pi + e + sqrt2 + ln2 == 7.9672352249818781, "whoopsie");
+
//----------------------------------------------------------------------------------
@@ -83,7 +85,7 @@ const double ln2 = 0.693147180559945309417;
template <class T> inline
T abs(T value)
{
- //static_assert(std::is_signed<T>::value, "");
+ //static_assert(std::is_signed_v<T>);
if (value < 0)
return -value; //operator "?:" caveat: may be different type than "value"
else
@@ -100,7 +102,7 @@ auto dist(T a, T b) //return type might be different than T, e.g. std::chrono::d
template <class T> inline
int sign(T value) //returns one of {-1, 0, 1}
{
- static_assert(std::is_signed<T>::value, "");
+ static_assert(std::is_signed_v<T>);
return value < 0 ? -1 : (value > 0 ? 1 : 0);
}
@@ -231,8 +233,8 @@ int64_t round(double d)
template <class N, class D> inline
auto integerDivideRoundUp(N numerator, D denominator)
{
- static_assert(std::is_integral<N>::value, "");
- static_assert(std::is_integral<D>::value, "");
+ static_assert(zen::IsInteger<N>::value);
+ static_assert(zen::IsInteger<D>::value);
assert(numerator > 0 && denominator > 0);
return (numerator + denominator - 1) / denominator;
}
diff --git a/zen/build_info.h b/zen/build_info.h
index 9b8b7fc0..e80f3721 100755
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -16,11 +16,11 @@
#endif
#ifdef ZEN_BUILD_32BIT
- static_assert(sizeof(void*) == 4, "");
+ static_assert(sizeof(void*) == 4);
#endif
#ifdef ZEN_BUILD_64BIT
- static_assert(sizeof(void*) == 8, "");
+ static_assert(sizeof(void*) == 8);
#endif
#endif //BUILD_INFO_H_5928539285603428657
diff --git a/zen/crc.h b/zen/crc.h
index 6f6ca996..ebac538f 100755
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -7,6 +7,7 @@
#ifndef CRC_H_23489275827847235
#define CRC_H_23489275827847235
+//boost, clean this mess up!
#include <boost/crc.hpp>
@@ -28,12 +29,12 @@ inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(),
template <class ByteIterator> inline
uint16_t getCrc16(ByteIterator first, ByteIterator last)
{
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1);
boost::crc_16_type result;
if (first != last)
result.process_bytes(&*first, last - first);
auto rv = result.checksum();
- static_assert(sizeof(rv) == sizeof(uint16_t), "");
+ static_assert(sizeof(rv) == sizeof(uint16_t));
return rv;
}
@@ -41,12 +42,12 @@ uint16_t getCrc16(ByteIterator first, ByteIterator last)
template <class ByteIterator> inline
uint32_t getCrc32(ByteIterator first, ByteIterator last)
{
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1);
boost::crc_32_type result;
if (first != last)
result.process_bytes(&*first, last - first);
auto rv = result.checksum();
- static_assert(sizeof(rv) == sizeof(uint32_t), "");
+ static_assert(sizeof(rv) == sizeof(uint32_t));
return rv;
}
}
diff --git a/zen/deprecate.h b/zen/deprecate.h
deleted file mode 100755
index 1f4e6ab4..00000000
--- a/zen/deprecate.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef DEPRECATE_H_234897087787348
-#define DEPRECATE_H_234897087787348
-
-//compiler macros: http://predef.sourceforge.net/precomp.html
-#ifdef __GNUC__
- #define ZEN_DEPRECATE __attribute__ ((deprecated))
-
-#else
- #error add your platform here!
-#endif
-
-#endif //DEPRECATE_H_234897087787348
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 0cbde150..2bb3fd26 100755
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -103,7 +103,7 @@ DirWatcher::~DirWatcher()
std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval) //throw FileError
{
- std::vector<char> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1));
+ std::vector<std::byte> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1));
ssize_t bytesRead = 0;
do
diff --git a/zen/error_log.h b/zen/error_log.h
index b6660850..0de88856 100755
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -13,7 +13,7 @@
#include <string>
#include "time.h"
#include "i18n.h"
-#include "string_base.h"
+#include "zstring.h"
namespace zen
@@ -26,13 +26,11 @@ enum MessageType
MSG_TYPE_FATAL_ERROR = 0x8,
};
-using MsgString = Zbase<wchar_t>; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items)
-
struct LogEntry
{
time_t time = 0;
MessageType type = MSG_TYPE_FATAL_ERROR;
- MsgString message;
+ Zstringw message; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items)
};
template <class String>
@@ -69,7 +67,7 @@ private:
template <class String> inline
void ErrorLog::logMsg(const String& text, MessageType type)
{
- entries_.push_back({ std::time(nullptr), type, copyStringTo<MsgString>(text) });
+ entries_.push_back({ std::time(nullptr), type, copyStringTo<Zstringw>(text) });
}
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 18c0ed26..fca1e3d8 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -52,18 +52,18 @@ Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
if (startsWith(itemPath, "/"))
{
- if (startsWith(itemPath, "/media/"))
+ if (startsWith(itemPath, "/media/"))
{
//Ubuntu: e.g. /media/zenju/DEVICE_NAME
if (const char* username = ::getenv("USER"))
if (startsWith(itemPath, std::string("/media/") + username + "/"))
return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- //Ubuntu: e.g. /media/cdrom0
+ //Ubuntu: e.g. /media/cdrom0
return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
}
- if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME
+ if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME
if (const char* username = ::getenv("USER"))
if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
@@ -372,7 +372,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr
}
catch (ErrorTargetExisting&)
{
-#if 0 //"Work around pen drive failing to change file name case" => enable if needed: https://www.freefilesync.org/forum/viewtopic.php?t=4279
+#if 0 //"Work around pen drive failing to change file name case" => enable if needed: https://freefilesync.org/forum/viewtopic.php?t=4279
const Zstring fileNameSrc = afterLast (pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
const Zstring fileNameTrg = afterLast (pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
@@ -411,17 +411,17 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim
using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"!
*/
struct ::timespec newTimes[2] = {};
- newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://www.freefilesync.org/forum/viewtopic.php?t=1701
+ newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: https://freefilesync.org/forum/viewtopic.php?t=1701
newTimes[1] = modTime; //modification time
if (procSl == ProcSymlink::FOLLOW)
{
//hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP:
- //https://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP)
+ //https://freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP)
if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0)
return;
- //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: https://www.freefilesync.org/forum/viewtopic.php?t=387
+ //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: https://freefilesync.org/forum/viewtopic.php?t=387
const int fdFile = ::open(itemPath.c_str(), O_WRONLY | O_APPEND); //2017-07-04: O_WRONLY | O_APPEND seems to avoid EOPNOTSUPP on gvfs SFTP!
if (fdFile == -1)
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open");
@@ -676,7 +676,7 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError,
//this triggers bugs on samba shares where the modification time is set to current time instead.
//Linux: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
// http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
- //OS X: https://www.freefilesync.org/forum/viewtopic.php?t=356
+ //OS X: https://freefilesync.org/forum/viewtopic.php?t=356
setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError
}
catch (const FileError& e)
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 1cbd970b..25dc93ce 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -132,7 +132,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E
size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
{
/*
- FFS 8.9-9.5 perf issues on macOS: https://www.freefilesync.org/forum/viewtopic.php?t=4808
+ FFS 8.9-9.5 perf issues on macOS: https://freefilesync.org/forum/viewtopic.php?t=4808
app-level buffering is essential to optimize random data sizes; e.g. "export file list":
=> big perf improvement on Windows, Linux. No significant improvement on macOS in tests
impact on stream-based file copy:
@@ -148,8 +148,8 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, Erro
assert(memBuf_.size() >= blockSize);
assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size());
- char* it = static_cast<char*>(buffer);
- char* const itEnd = it + bytesToRead;
+ auto it = static_cast<std::byte*>(buffer);
+ const auto itEnd = it + bytesToRead;
for (;;)
{
const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_);
@@ -169,7 +169,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, Erro
if (bytesRead == 0) //end of file
break;
}
- return it - static_cast<char*>(buffer);
+ return it - static_cast<std::byte*>(buffer);
}
//----------------------------------------------------------------------------------------------------
@@ -253,8 +253,8 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
assert(memBuf_.size() >= blockSize);
assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size());
- const char* it = static_cast<const char*>(buffer);
- const char* const itEnd = it + bytesToWrite;
+ auto it = static_cast<const std::byte*>(buffer);
+ const auto itEnd = it + bytesToWrite;
for (;;)
{
if (memBuf_.size() - bufPos_ < blockSize) //support memBuf_.size() > blockSize to reduce memmove()s, but perf test shows: not really needed!
diff --git a/zen/file_io.h b/zen/file_io.h
index 5b0b8cb0..d05d97db 100755
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -66,7 +66,7 @@ private:
const IOCallback notifyUnbufferedIO_; //throw X
- std::vector<char> memBuf_ = std::vector<char>(getBlockSize());
+ std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
size_t bufPosEnd_= 0;
};
@@ -95,7 +95,7 @@ private:
IOCallback notifyUnbufferedIO_; //throw X
- std::vector<char> memBuf_ = std::vector<char>(getBlockSize());
+ std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
size_t bufPos_ = 0;
size_t bufPosEnd_ = 0;
};
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
deleted file mode 100755
index 10b66233..00000000
--- a/zen/fixed_list.h
+++ /dev/null
@@ -1,240 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FIXED_LIST_H_01238467085684139453534
-#define FIXED_LIST_H_01238467085684139453534
-
-#include <cassert>
-#include <iterator>
-#include "stl_tools.h"
-
-
-namespace zen
-{
-//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types
-//-> no iterator invalidation after emplace_back()
-
-template <class T>
-class FixedList
-{
- struct Node
- {
- template <class... Args>
- Node(Args&& ... args) : val(std::forward<Args>(args)...) {}
-
- Node* next = nullptr; //singly-linked list is sufficient
- T val;
- };
-
-public:
- FixedList() {}
-
- ~FixedList() { clear(); }
-
- template <class NodeT, class U>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, U>
- {
- public:
- FixedIterator(NodeT* it = nullptr) : it_(it) {}
- FixedIterator& operator++() { it_ = it_->next; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
- inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
- U& operator* () const { return it_->val; }
- U* operator->() const { return &it_->val; }
- private:
- NodeT* it_;
- };
-
- using value_type = T;
- using iterator = FixedIterator< Node, T>;
- using const_iterator = FixedIterator<const Node, const T>;
- using reference = T&;
- using const_reference = const T&;
-
- iterator begin() { return firstInsert_; }
- iterator end () { return iterator(); }
-
- const_iterator begin() const { return firstInsert_; }
- const_iterator end () const { return const_iterator(); }
-
- //const_iterator cbegin() const { return firstInsert_; }
- //const_iterator cend () const { return const_iterator(); }
-
- reference front() { return firstInsert_->val; }
- const_reference front() const { return firstInsert_->val; }
-
- reference& back() { return lastInsert_->val; }
- const_reference& back() const { return lastInsert_->val; }
-
- template <class... Args>
- void emplace_back(Args&& ... args)
- {
- Node* newNode = new Node(std::forward<Args>(args)...);
-
- if (!lastInsert_)
- {
- assert(!firstInsert_ && sz_ == 0);
- firstInsert_ = lastInsert_ = newNode;
- }
- else
- {
- assert(lastInsert_->next == nullptr);
- lastInsert_->next = newNode;
- lastInsert_ = newNode;
- }
- ++sz_;
- }
-
- template <class Predicate>
- void remove_if(Predicate pred)
- {
- Node* prev = nullptr;
- Node* ptr = firstInsert_;
-
- while (ptr)
- if (pred(ptr->val))
- {
- Node* next = ptr->next;
-
- delete ptr;
- assert(sz_ > 0);
- --sz_;
-
- ptr = next;
-
- if (prev)
- prev->next = next;
- else
- firstInsert_ = next;
- if (!next)
- lastInsert_ = prev;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
- }
- }
-
- void clear()
- {
- Node* ptr = firstInsert_;
- while (ptr)
- {
- Node* next = ptr->next;
- delete ptr;
- ptr = next;
- }
-
- sz_ = 0;
- firstInsert_ = lastInsert_ = nullptr;
- }
-
- bool empty() const { return sz_ == 0; }
-
- size_t size() const { return sz_; }
-
- void swap(FixedList& other)
- {
- std::swap(firstInsert_, other.firstInsert_);
- std::swap(lastInsert_, other.lastInsert_);
- std::swap(sz_, other.sz_);
- }
-
-private:
- FixedList (const FixedList&) = delete;
- FixedList& operator=(const FixedList&) = delete;
-
- Node* firstInsert_ = nullptr;
- Node* lastInsert_ = nullptr; //point to last insertion; required by efficient emplace_back()
- size_t sz_ = 0;
-};
-
-
-//just as fast as FixedList, but simpler, more CPU-cache-friendly => superseeds FixedList!
-template <class T>
-class FixedVector
-{
-public:
- FixedVector() {}
-
- /*
- class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
-
- template <class V>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed
- {
- public:
- FixedIterator(std::vector<std::unique_ptr<T>>& cont, size_t pos) : cont_(cont), pos_(pos) {}
- FixedIterator& operator++() { ++pos_; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, EndIterator) { return lhs.pos_ == lhs.cont_.size(); }
- inline friend bool operator!=(const FixedIterator& lhs, EndIterator) { return !(lhs == EndIterator()); }
- V& operator* () const { return *cont_[pos_]; }
- V* operator->() const { return &*cont_[pos_]; }
- private:
- std::vector<std::unique_ptr<T>>& cont_;
- size_t pos_ = 0;
- };
- */
-
- template <class IterImpl, class V>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed
- {
- public:
- FixedIterator(IterImpl it) : it_(it) {}
- FixedIterator& operator++() { ++it_; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
- inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
- V& operator* () const { return **it_; }
- V* operator->() const { return &** it_; }
- private:
- IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length!
- };
-
- using value_type = T;
- using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator, T>;
- using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>;
- using reference = T&;
- using const_reference = const T&;
-
- iterator begin() { return items_.begin(); }
- iterator end () { return items_.end (); }
-
- const_iterator begin() const { return items_.begin(); }
- const_iterator end () const { return items_.end (); }
-
- reference front() { return *items_.front(); }
- const_reference front() const { return *items_.front(); }
-
- reference& back() { return *items_.back(); }
- const_reference& back() const { return *items_.back(); }
-
- template <class... Args>
- void emplace_back(Args&& ... args)
- {
- items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
- }
-
- template <class Predicate>
- void remove_if(Predicate pred)
- {
- erase_if(items_, [&](const std::unique_ptr<T>& p) { return pred(*p); });
- }
-
- void clear() { items_.clear(); }
- bool empty() const { return items_.empty(); }
- size_t size () const { return items_.size(); }
- void swap(FixedVector& other) { items_.swap(other.items_); }
-
-private:
- FixedVector (const FixedVector&) = delete;
- FixedVector& operator=(const FixedVector&) = delete;
-
- std::vector<std::unique_ptr<T>> items_;
-};
-}
-
-#endif //FIXED_LIST_H_01238467085684139453534
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 931a29be..3e75278b 100755
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -193,7 +193,7 @@ std::wstring zen::formatUtcToLocalTime(time_t utcTime)
auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; };
TimeComp loc = getLocalTime(utcTime);
-
+
std::wstring dateString = formatTime<std::wstring>(L"%x %X", loc);
return !dateString.empty() ? dateString : errorMsg();
}
diff --git a/zen/globals.h b/zen/globals.h
index 2066c380..10975414 100755
--- a/zen/globals.h
+++ b/zen/globals.h
@@ -21,10 +21,12 @@ class Global
public:
Global()
{
- static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever");
+ static_assert(std::is_trivially_destructible_v<Pod>, "this memory needs to live forever");
assert(!pod_.inst && !pod_.spinLock); //we depend on static zero-initialization!
}
+
explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
+
~Global() { set(nullptr); }
std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!)
@@ -60,7 +62,6 @@ private:
//serialize access; can't use std::mutex: has non-trival destructor
} pod_;
};
-
}
#endif //GLOBALS_H_8013740213748021573485
diff --git a/zen/guid.h b/zen/guid.h
index 6cffc708..50ca64d2 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -8,16 +8,7 @@
#define GUID_H_80425780237502345
#include <string>
-
-#ifdef __GNUC__ //boost should clean this mess up
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-#include <boost/uuid/uuid_generators.hpp>
-#ifdef __GNUC__
- #pragma GCC diagnostic pop
-#endif
-
+ #include <boost/uuid/uuid_generators.hpp>
namespace zen
{
@@ -28,6 +19,7 @@ std::string generateGUID() //creates a 16-byte GUID
// retrieve GUID: 0.13µs per call
//generator is only thread-safe like an int => keep thread-local
thread_local boost::uuids::random_generator gen;
+ static_assert(boost::uuids::uuid::static_size() == 16);
const boost::uuids::uuid nativeRep = gen();
return std::string(nativeRep.begin(), nativeRep.end());
}
diff --git a/zen/http.cpp b/zen/http.cpp
new file mode 100755
index 00000000..d06d3309
--- /dev/null
+++ b/zen/http.cpp
@@ -0,0 +1,376 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "http.h"
+
+ #include "socket.h"
+
+using namespace zen;
+
+
+class HttpInputStream::Impl
+{
+public:
+ Impl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError
+ const std::vector<std::pair<std::string, std::string>>* postParams) : //issue POST if bound, GET otherwise
+ notifyUnbufferedIO_(notifyUnbufferedIO)
+ {
+ ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ );
+
+ const Zstring urlFmt = afterFirst(url, Zstr("://"), IF_MISSING_RETURN_NONE);
+ const Zstring server = beforeFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_ALL);
+ const Zstring page = Zstr('/') + afterFirst(urlFmt, Zstr('/'), IF_MISSING_RETURN_NONE);
+
+ const bool useTls = [&]
+ {
+ if (startsWith(url, Zstr("http://"), CmpAsciiNoCase()))
+ return false;
+ if (startsWith(url, Zstr("https://"), CmpAsciiNoCase()))
+ return true;
+ throw SysError(L"URL uses unexpected protocol.");
+ }();
+
+ assert(!useTls); //not supported by our plain socket!
+ (void)useTls;
+
+ socket_ = std::make_unique<Socket>(server, Zstr("http")); //throw SysError
+ //HTTP default port: 80, see %WINDIR%\system32\drivers\etc\services
+
+ std::map<std::string, std::string, LessAsciiNoCase> headers;
+ headers["Host" ] = utfTo<std::string>(server); //only required for HTTP/1.1
+ headers["User-Agent"] = utfTo<std::string>(userAgent);
+ headers["Accept" ] = "*/*"; //won't hurt?
+
+ const std::string postBuf = postParams ? xWwwFormUrlEncode(*postParams) : "";
+
+ if (!postParams) //HTTP GET
+ headers["Pragma"] = "no-cache"; //HTTP 1.0 only! superseeded by "Cache-Control"
+ //consider internetIsAlive() test; not relevant for POST (= never cached)
+ else //HTTP POST
+ {
+ headers["Content-type"] = "application/x-www-form-urlencoded";
+ headers["Content-Length"] = numberTo<std::string>(postBuf.size());
+ }
+
+ //https://www.w3.org/Protocols/HTTP/1.0/spec.html#Request-Line
+ std::string msg = (postParams ? "POST " : "GET ") + utfTo<std::string>(page) + " HTTP/1.0\r\n";
+ for (const auto& item : headers)
+ msg += item.first + ": " + item.second + "\r\n";
+ msg += "\r\n";
+ msg += postBuf;
+
+ //send request
+ for (size_t bytesToSend = msg.size(); bytesToSend > 0;)
+ {
+ int bytesSent = 0;
+ for (;;)
+ {
+ bytesSent = ::send(socket_->get(), //_In_ SOCKET s,
+ &*(msg.end() - bytesToSend), //_In_ const char *buf,
+ static_cast<int>(bytesToSend), //_In_ int len,
+ 0); //_In_ int flags
+ if (bytesSent >= 0 || errno != EINTR)
+ break;
+ }
+ if (bytesSent < 0)
+ THROW_LAST_SYS_ERROR_WSA(L"send");
+ if (bytesSent > static_cast<int>(bytesToSend))
+ throw SysError(L"send: buffer overflow.");
+ if (bytesSent == 0)
+ throw SysError(L"send: zero bytes processed");
+
+ bytesToSend -= bytesSent;
+ }
+ if (::shutdown(socket_->get(), SHUT_WR) != 0)
+ THROW_LAST_SYS_ERROR_WSA(L"shutdown");
+
+ //receive response:
+ std::string headBuf;
+ const std::string headerDelim = "\r\n\r\n";
+ for (std::string buf;;)
+ {
+ const size_t blockSize = std::min(static_cast<size_t>(1024), memBuf_.size()); //smaller block size: try to only read header part
+ buf.resize(buf.size() + blockSize);
+ const size_t bytesReceived = tryRead(&*(buf.end() - blockSize), blockSize); //throw SysError
+ buf.resize(buf.size() - blockSize + bytesReceived); //caveat: unsigned arithmetics
+
+ if (contains(buf, headerDelim))
+ {
+ headBuf = beforeFirst(buf, headerDelim, IF_MISSING_RETURN_NONE);
+ const std::string bodyBuf = afterFirst (buf, headerDelim, IF_MISSING_RETURN_NONE);
+ //put excess bytes into instance buffer for body retrieval
+ assert(bufPos_ == 0 && bufPosEnd_ == 0);
+ bufPosEnd_ = bodyBuf.size();
+ std::copy(bodyBuf.begin(), bodyBuf.end(), reinterpret_cast<char*>(&memBuf_[0]));
+ break;
+ }
+ if (bytesReceived == 0)
+ break;
+ }
+ //parse header
+ const std::string statusBuf = beforeFirst(headBuf, "\r\n", IF_MISSING_RETURN_ALL);
+ const std::string headersBuf = afterFirst (headBuf, "\r\n", IF_MISSING_RETURN_NONE);
+
+ const std::vector<std::string> statusItems = split(statusBuf, ' ', SplitType::ALLOW_EMPTY); //HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ if (statusItems.size() < 2 || !startsWith(statusItems[0], "HTTP/"))
+ throw SysError(L"Invalid HTTP response: \"" + utfTo<std::wstring>(statusBuf) + L"\"");
+
+ statusCode_ = stringTo<int>(statusItems[1]);
+
+ for (const std::string& line : split(headersBuf, "\r\n", SplitType::SKIP_EMPTY))
+ responseHeaders_[trimCpy(beforeFirst(line, ":", IF_MISSING_RETURN_ALL))] =
+ /**/ trimCpy(afterFirst (line, ":", IF_MISSING_RETURN_NONE));
+
+ //try to get "Content-Length" header if available
+ if (const std::string* value = getHeader("Content-Length"))
+ contentRemaining_ = stringTo<int64_t>(*value) - (bufPosEnd_ - bufPos_);
+ }
+
+ ~Impl() { cleanup(); }
+
+
+ const int getStatusCode() const { return statusCode_; }
+
+ const std::string* getHeader(const std::string& name) const
+ {
+ auto it = responseHeaders_.find(name);
+ return it != responseHeaders_.end() ? &it->second : nullptr;
+ }
+
+ size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream!
+ {
+ const size_t blockSize = getBlockSize();
+ assert(memBuf_.size() >= blockSize);
+ assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size());
+
+ auto it = static_cast<std::byte*>(buffer);
+ const auto itEnd = it + bytesToRead;
+ for (;;)
+ {
+ const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_);
+ std::memcpy(it, &memBuf_[0] + bufPos_, junkSize);
+ bufPos_ += junkSize;
+ it += junkSize;
+
+ if (it == itEnd)
+ break;
+ //--------------------------------------------------------------------
+ const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
+ bufPos_ = 0;
+ bufPosEnd_ = bytesRead;
+
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
+
+ if (bytesRead == 0) //end of file
+ break;
+ }
+ return it - static_cast<std::byte*>(buffer);
+ }
+
+ size_t getBlockSize() const { return 64 * 1024; }
+
+private:
+ size_t tryRead(void* buffer, size_t bytesToRead) //throw SysError; may return short, only 0 means EOF!
+ {
+ if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ assert(bytesToRead <= getBlockSize()); //block size might be 1000 while reading HTTP header
+
+ if (contentRemaining_ >= 0)
+ {
+ if (contentRemaining_ == 0)
+ return 0;
+ bytesToRead = static_cast<size_t>(std::min(static_cast<int64_t>(bytesToRead), contentRemaining_)); //[!] contentRemaining_ > 4 GB possible!
+ }
+ int bytesReceived = 0;
+ for (;;)
+ {
+ bytesReceived = ::recv(socket_->get(), //_In_ SOCKET s,
+ static_cast<char*>(buffer), //_Out_ char *buf,
+ static_cast<int>(bytesToRead), //_In_ int len,
+ 0); //_In_ int flags
+ if (bytesReceived >= 0 || errno != EINTR)
+ break;
+ }
+ if (bytesReceived < 0)
+ THROW_LAST_SYS_ERROR_WSA(L"recv");
+ if (static_cast<size_t>(bytesReceived) > bytesToRead) //better safe than sorry
+ throw SysError(L"HttpInputStream::tryRead: buffer overflow.");
+
+ if (contentRemaining_ >= 0)
+ contentRemaining_ -= bytesReceived;
+
+ if (bytesReceived == 0 && contentRemaining_ > 0)
+ throw SysError(replaceCpy<std::wstring>(L"HttpInputStream::tryRead: incomplete server response; %x more bytes expected.", L"%x", numberTo<std::wstring>(contentRemaining_)));
+
+ return bytesReceived; //"zero indicates end of file"
+ }
+
+ Impl (const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ void cleanup()
+ {
+ }
+
+ std::unique_ptr<Socket> socket_; //*bound* after constructor has run
+ int statusCode_ = 0;
+ std::map<std::string, std::string, LessAsciiNoCase> responseHeaders_;
+
+ int64_t contentRemaining_ = -1; //consider "Content-Length" if available
+
+ const IOCallback notifyUnbufferedIO_; //throw X
+
+ std::vector<std::byte> memBuf_ = std::vector<std::byte>(getBlockSize());
+ size_t bufPos_ = 0; //buffered I/O; see file_io.cpp
+ size_t bufPosEnd_ = 0; //
+};
+
+
+HttpInputStream::HttpInputStream(std::unique_ptr<Impl>&& pimpl) : pimpl_(std::move(pimpl)) {}
+
+HttpInputStream::~HttpInputStream() {}
+
+size_t HttpInputStream::read(void* buffer, size_t bytesToRead) { return pimpl_->read(buffer, bytesToRead); } //throw SysError, X; return "bytesToRead" bytes unless end of stream!
+
+size_t HttpInputStream::getBlockSize() const { return pimpl_->getBlockSize(); }
+
+std::string HttpInputStream::readAll() { return bufferedLoad<std::string>(*pimpl_); } //throw SysError, X;
+
+
+namespace
+{
+std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO, //throw SysError
+ const std::vector<std::pair<std::string, std::string>>* postParams) //issue POST if bound, GET otherwise
+{
+ Zstring urlRed = url;
+ //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ for (int redirects = 0; redirects < 6; ++redirects)
+ {
+ auto response = std::make_unique<HttpInputStream::Impl>(urlRed, userAgent, notifyUnbufferedIO, postParams); //throw SysError
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ const int statusCode = response->getStatusCode();
+ if (statusCode / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ const std::string* value = response->getHeader("Location");
+ if (!value || value->empty())
+ throw SysError(L"Unresolvable redirect. No target Location.");
+
+ urlRed = utfTo<Zstring>(*value);
+ }
+ else
+ {
+ if (statusCode != 200) //HTTP_STATUS_OK
+ throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(statusCode)));
+ //e.g. 404 - HTTP_STATUS_NOT_FOUND
+
+ return response;
+ }
+ }
+ throw SysError(L"Too many redirects.");
+}
+
+
+//encode into "application/x-www-form-urlencoded"
+std::string urlencode(const std::string& str)
+{
+ std::string out;
+ for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/master/ext/standard/url.c#L500
+ if (c == ' ')
+ out += '+';
+ else if (('0' <= c && c <= '9') ||
+ ('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z') ||
+ c == '-' || c == '.' || c == '_') //note: "~" is encoded by PHP!
+ out += c;
+ else
+ {
+ const std::pair<char, char> hex = hexify(c);
+
+ out += '%';
+ out += hex.first;
+ out += hex.second;
+ }
+ return out;
+}
+
+
+std::string urldecode(const std::string& str)
+{
+ std::string out;
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ const char c = str[i];
+ if (c == '+')
+ out += ' ';
+ else if (c == '%' && str.size() - i >= 3 &&
+ isHexDigit(str[i + 1]) &&
+ isHexDigit(str[i + 2]))
+ {
+ out += unhexify(str[i + 1], str[i + 2]);
+ i += 2;
+ }
+ else
+ out += c;
+ }
+ return out;
+}
+}
+
+
+std::string zen::xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs)
+{
+ std::string output;
+ for (const auto& pair : paramPairs)
+ output += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
+ //encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
+ if (!output.empty())
+ output.pop_back();
+ return output;
+}
+
+
+std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string& str)
+{
+ std::vector<std::pair<std::string, std::string>> output;
+
+ for (const std::string& nvPair : split(str, '&', SplitType::SKIP_EMPTY))
+ output.emplace_back(urldecode(beforeFirst(nvPair, '=', IF_MISSING_RETURN_ALL)),
+ urldecode(afterFirst (nvPair, '=', IF_MISSING_RETURN_NONE)));
+ return output;
+}
+
+
+HttpInputStream zen::sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO,
+ const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
+{
+ return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, &postParams); //throw SysError
+}
+
+
+HttpInputStream zen::sendHttpGet(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO) //throw SysError
+{
+ return sendHttpRequestImpl(url, userAgent, notifyUnbufferedIO, nullptr); //throw SysError
+}
+
+
+bool zen::internetIsAlive() //noexcept
+{
+ try
+ {
+ auto response = std::make_unique<HttpInputStream::Impl>(Zstr("http://www.google.com/"),
+ Zstr("FreeFileSync"),
+ nullptr /*notifyUnbufferedIO*/,
+ nullptr /*postParams*/); //throw SysError
+ const int statusCode = response->getStatusCode();
+
+ //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
+ return statusCode / 100 == 2 || //e.g. 200
+ statusCode / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive!
+ }
+ catch (SysError&) { return false; }
+}
diff --git a/wx+/http.h b/zen/http.h
index bbfa3a74..5d84be2c 100755
--- a/wx+/http.h
+++ b/zen/http.h
@@ -7,16 +7,15 @@
#ifndef HTTP_H_879083425703425702
#define HTTP_H_879083425703425702
-#include <zen/file_error.h>
+#include <zen/zstring.h>
+#include <zen/sys_error.h>
#include <zen/serialize.h>
namespace zen
{
/*
- TREAD-SAFETY
- ------------
- Windows: WinInet-based => may be called from worker thread, supports HTTPS
- Linux: wxWidgets-based => don't call from worker thread
+ - thread-safe! (Window/Linux/macOS)
+ - HTTPS supported only for Windows
*/
class HttpInputStream
{
@@ -29,7 +28,7 @@ public:
class Impl;
HttpInputStream(std::unique_ptr<Impl>&& pimpl);
- HttpInputStream(HttpInputStream&&) = default;
+ HttpInputStream(HttpInputStream&&) noexcept = default;
~HttpInputStream();
private:
@@ -37,9 +36,9 @@ private:
};
-HttpInputStream sendHttpGet (const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO); //throw SysError
-HttpInputStream sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const IOCallback& notifyUnbufferedIO,
- const std::vector<std::pair<std::string, std::string>>& postParams); //throw SysError
+HttpInputStream sendHttpGet (const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/); //throw SysError
+HttpInputStream sendHttpPost(const Zstring& url, const Zstring& userAgent, const IOCallback& notifyUnbufferedIO /*throw X*/, //
+ const std::vector<std::pair<std::string, std::string>>& postParams);
bool internetIsAlive(); //noexcept
std::string xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs);
diff --git a/zen/i18n.h b/zen/i18n.h
index b67a3bb6..45762861 100755
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -95,7 +95,7 @@ std::wstring translate(const std::wstring& text)
template <class T> inline
std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
{
- static_assert(sizeof(n) <= sizeof(int64_t), "");
+ static_assert(sizeof(n) <= sizeof(int64_t));
const auto n64 = static_cast<int64_t>(n);
assert(contains(plural, L"%x"));
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
new file mode 100755
index 00000000..5a0013b3
--- /dev/null
+++ b/zen/legacy_compiler.h
@@ -0,0 +1,89 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef LEGACY_COMPILER_H_839567308565656789
+#define LEGACY_COMPILER_H_839567308565656789
+
+ #include <memory>
+ #include <cstddef> //std::byte
+
+
+namespace std
+{
+//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html
+//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
+
+#ifndef __cpp_lib_type_trait_variable_templates //GCC 7.1
+ template<class T, class U> constexpr bool is_same_v = is_same<T, U>::value;
+ template<class T> constexpr bool is_const_v = is_const<T>::value;
+ template<class T> constexpr bool is_signed_v = is_signed<T>::value;
+ template<class T> constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value;
+#endif
+
+#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //GCC doesn't define __cpp_lib_raw_memory_algorithms
+template<class T> void destroy_at(T* p) { p->~T(); }
+
+template< class ForwardIt >
+void destroy(ForwardIt first, ForwardIt last)
+{
+ for (; first != last; ++first)
+ std::destroy_at(std::addressof(*first));
+}
+
+template<class InputIt, class ForwardIt>
+ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt trg_first)
+{
+ typedef typename std::iterator_traits<ForwardIt>::value_type Value;
+ ForwardIt current = trg_first;
+ try
+ {
+ for (; first != last; ++first, ++current)
+ ::new (static_cast<void*>(std::addressof(*current))) Value(std::move(*first));
+ return current;
+ }
+ catch (...)
+ {
+ for (; trg_first != current; ++trg_first)
+ trg_first->~Value();
+ throw;
+ }
+}
+#endif
+
+#ifndef __cpp_lib_apply //GCC 7.1
+template <class F, class T0, class T1, class T2>
+constexpr decltype(auto) apply(F&& f, std::tuple<T0, T1, T2>& t) { return f(std::get<0>(t), std::get<1>(t), std::get<2>(t)); }
+#endif
+
+#if __GNUC__ < 7 || (__GNUC__ == 7 && __GNUC_MINOR__ < 1) //__cpp_lib_byte not defined before GCC 7.3 but supported earlier
+ typedef unsigned char byte;
+#endif
+
+#ifndef __cpp_lib_bool_constant //GCC 6.1
+ template<bool B> using bool_constant = integral_constant<bool, B>;
+#endif
+
+//================================================================================
+
+}
+namespace __cxxabiv1
+{
+struct __cxa_eh_globals;
+extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
+}
+namespace std
+{
+inline int uncaught_exceptions_legacy_hack() noexcept
+{
+ return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
+}
+#ifndef __cpp_lib_uncaught_exceptions //GCC 6.1
+inline int uncaught_exceptions() noexcept { return uncaught_exceptions_legacy_hack(); }
+#endif
+
+}
+
+#endif //LEGACY_COMPILER_H_839567308565656789
diff --git a/zen/optional.h b/zen/optional.h
index 88928ac0..e4605c5f 100755
--- a/zen/optional.h
+++ b/zen/optional.h
@@ -7,7 +7,6 @@
#ifndef OPTIONAL_H_2857428578342203589
#define OPTIONAL_H_2857428578342203589
-//#include <cassert>
#include <type_traits>
@@ -51,16 +50,6 @@ public:
~Opt() { if (T* val = get()) val->~T(); }
- Opt& operator=(NoValue) //support assignment to Opt<const T>
- {
- if (T* val = get())
- {
- valid_ = false;
- val->~T();
- }
- return *this;
- }
-
Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe
{
if (T* val = get())
@@ -81,6 +70,16 @@ public:
return *this;
}
+ Opt& operator=(NoValue) //support assignment to Opt<const T>
+ {
+ if (T* val = get())
+ {
+ valid_ = false;
+ val->~T();
+ }
+ return *this;
+ }
+
explicit operator bool() const { return valid_; } //thank you, C++11!!!
const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; }
diff --git a/zen/perf.h b/zen/perf.h
index 2006bdab..40b6533d 100755
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -8,8 +8,8 @@
#define PERF_H_83947184145342652456
#include <chrono>
-#include "deprecate.h"
#include "scope_guard.h"
+#include "string_tools.h"
#include <iostream>
@@ -31,7 +31,7 @@ namespace zen
class PerfTimer
{
public:
- ZEN_DEPRECATE PerfTimer() {}
+ [[deprecated]] PerfTimer() {}
~PerfTimer() { if (!resultShown_) showResult(); }
@@ -75,7 +75,8 @@ public:
if (wasRunning) pause(); //don't include call to MessageBox()!
ZEN_ON_SCOPE_EXIT(if (wasRunning) resume());
- std::clog << "Perf: duration: " << timeMs() << " ms\n";
+ const std::string msg = numberTo<std::string>(timeMs()) + " ms";
+ std::clog << "Perf: duration: " << msg << "\n";
resultShown_ = true;
}
diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h
new file mode 100755
index 00000000..1a67c452
--- /dev/null
+++ b/zen/ring_buffer.h
@@ -0,0 +1,230 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef RING_BUFFER_H_01238467085684139453534
+#define RING_BUFFER_H_01238467085684139453534
+
+#include <cassert>
+#include <vector>
+#include <stdexcept>
+#include "type_traits.h"
+#include "scope_guard.h"
+
+
+namespace zen
+{
+//basically a std::deque<> but with a non-garbage implementation => circular buffer with std::vector<>-like exponential growth!
+//https://stackoverflow.com/questions/39324192/why-is-an-stl-deque-not-implemented-as-just-a-circular-vector
+
+template <class T>
+class RingBuffer
+{
+public:
+ RingBuffer() {}
+
+ RingBuffer(RingBuffer&& tmp) noexcept : rawMem_(std::move(tmp.rawMem_)), capacity_(tmp.capacity_), bufStart_(tmp.bufStart_), size_(tmp.size_)
+ {
+ tmp.capacity_ = tmp.bufStart_ = tmp.size_ = 0;
+ }
+ RingBuffer& operator=(RingBuffer&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!!
+
+ using value_type = T;
+ using reference = T&;
+ using const_reference = const T&;
+
+ size_t size() const { return size_; }
+ bool empty() const { return size_ == 0; }
+
+ ~RingBuffer() { clear(); }
+
+ reference front() { return getBufPtr()[bufStart_]; }
+ const_reference front() const { return getBufPtr()[bufStart_]; }
+
+ template <class U>
+ void push_back(U&& value)
+ {
+ checkInvariants();
+ reserve(size_ + 1); //throw ?
+ ::new (getBufPtr() + getBufPos(size_)) T(std::forward<U>(value)); //throw ?
+ ++size_;
+ }
+
+ void pop_front()
+ {
+ checkInvariants();
+ if (empty())
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ front().~T();
+ ++bufStart_;
+ --size_;
+
+ if (size_ == 0 || bufStart_ == capacity_)
+ bufStart_ = 0;
+ }
+
+ void clear()
+ {
+ checkInvariants();
+
+ const size_t frontSize = std::min(size_, capacity_ - bufStart_);
+
+ std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize);
+ std::destroy(getBufPtr(), getBufPtr() + size_ - frontSize);
+ bufStart_ = size_ = 0;
+ }
+
+ template <class Iterator>
+ void insert_back(Iterator first, Iterator last) //throw ? (strong exception-safety!)
+ {
+ checkInvariants();
+
+ const size_t len = last - first;
+ reserve(size_ + len); //throw ?
+
+ const size_t endPos = getBufPos(size_);
+ const size_t tailSize = std::min(len, capacity_ - endPos);
+
+ std::uninitialized_copy(first, first + tailSize, getBufPtr() + endPos); //throw ?
+ ZEN_ON_SCOPE_FAIL(std::destroy(first, first + tailSize));
+ std::uninitialized_copy(first + tailSize, last, getBufPtr()); //throw ?
+
+ size_ += len;
+ }
+
+ //contract: last - first <= size()
+ template <class Iterator>
+ void extract_front(Iterator first, Iterator last) //throw ? strongly exception-safe! (but only basic exception safety for [first, last) range)
+ {
+ checkInvariants();
+
+ const size_t len = last - first;
+ if (size_ < len)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ const size_t frontSize = std::min(len, capacity_ - bufStart_);
+
+ auto itTrg = std::copy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize, first); //throw ?
+ /**/ std::copy(getBufPtr(), getBufPtr() + len - frontSize, itTrg); //
+
+ std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize);
+ std::destroy(getBufPtr(), getBufPtr() + len - frontSize);
+
+ bufStart_ += len;
+ size_ -= len;
+
+ if (size_ == 0)
+ bufStart_ = 0;
+ else if (bufStart_ >= capacity_)
+ bufStart_ -= capacity_;
+ }
+
+ void swap(RingBuffer& other)
+ {
+ std::swap(rawMem_, other.rawMem_);
+ std::swap(capacity_, other.capacity_);
+ std::swap(bufStart_, other.bufStart_);
+ std::swap(size_, other.size_);
+ }
+
+ void reserve(size_t minSize) //throw ? (strong exception-safety!)
+ {
+ if (minSize > capacity_)
+ {
+ const size_t newCapacity = std::max(minSize + minSize / 2, minSize); //no minimum capacity: just like std::vector<> implementation
+
+ RingBuffer newBuf(newCapacity); //throw ?
+
+ T* itTrg = reinterpret_cast<T*>(newBuf.rawMem_.get());
+
+ const size_t frontSize = std::min(size_, capacity_ - bufStart_);
+
+ itTrg = uninitializedMoveIfNoexcept(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize, itTrg); //throw ?
+ newBuf.size_ = frontSize; //pass ownership
+ /**/ uninitializedMoveIfNoexcept(getBufPtr(), getBufPtr() + size_ - frontSize, itTrg); //throw ?
+ newBuf.size_ = size_; //
+
+ newBuf.swap(*this);
+ }
+ }
+
+ const T& operator[](size_t offset) const
+ {
+ assert(offset < size()); //design by contract! no runtime check!
+ return getBufPtr()[getBufPos(offset)];
+ }
+
+ T& operator[](size_t offset) { return const_cast<T&>(static_cast<const RingBuffer*>(this)->operator[](offset)); }
+
+ template <class Container, class Value>
+ class Iterator
+ {
+ public:
+ Iterator(Container& container, size_t offset) : container_(&container), offset_(offset) {}
+ Iterator& operator++() { ++offset_; return *this; }
+ inline friend bool operator==(const Iterator& lhs, const Iterator& rhs) { assert(lhs.container_ == rhs.container_); return lhs.offset_ == rhs.offset_; }
+ inline friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return !(lhs == rhs); }
+ Value& operator* () const { return (*container_)[offset_]; }
+ Value* operator->() const { return &(*container_)[offset_]; }
+ private:
+ Container* container_ = nullptr;
+ size_t offset_ = 0;
+ };
+ using iterator = Iterator< RingBuffer, T>;
+ using const_iterator = Iterator<const RingBuffer, const T>;
+
+ iterator begin() { return { *this, 0 }; }
+ iterator end () { return { *this, size_ }; }
+
+ const_iterator begin() const { return { *this, 0 }; }
+ const_iterator end () const { return { *this, size_ }; }
+
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend () const { return end (); }
+
+private:
+ RingBuffer (const RingBuffer&) = delete; //wait until there is a reason to copy a RingBuffer
+ RingBuffer& operator=(const RingBuffer&) = delete; //
+
+ RingBuffer(size_t capacity) :
+ rawMem_(static_cast<std::byte*>(::operator new (capacity * sizeof(T)))), //throw std::bad_alloc
+ capacity_(capacity) {}
+
+ struct FreeStoreDelete { void operator()(std::byte* p) const { ::operator delete (p); } };
+
+ T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); }
+ const T* getBufPtr() const { return reinterpret_cast<T*>(rawMem_.get()); }
+
+ //unlike pure std::uninitialized_move, this one allows for strong exception-safety!
+ static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg)
+ {
+ return uninitializedMoveIfNoexcept(first, last, firstTrg, std::is_nothrow_move_constructible<T>());
+ }
+ static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg, std::true_type ) { return std::uninitialized_move(first, last, firstTrg); }
+ static T* uninitializedMoveIfNoexcept(T* first, T* last, T* firstTrg, std::false_type) { return std::uninitialized_copy(first, last, firstTrg); } //throw ?
+
+ void checkInvariants()
+ {
+ assert(bufStart_ == 0 || bufStart_ < capacity_);
+ assert(size_ <= capacity_);
+ }
+
+ size_t getBufPos(size_t offset) const //< capacity_
+ {
+ size_t bufPos = bufStart_ + offset;
+ if (bufPos >= capacity_)
+ bufPos -= capacity_;
+ return bufPos;
+ }
+
+ std::unique_ptr<std::byte, FreeStoreDelete> rawMem_;
+ size_t capacity_ = 0; //as number of T
+ size_t bufStart_ = 0; //< capacity_
+ size_t size_ = 0; //<= capacity_
+};
+}
+
+#endif //RING_BUFFER_H_01238467085684139453534
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 6a732c9f..d056bb2a 100755
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -9,23 +9,7 @@
#include <cassert>
#include <exception>
-#include "type_tools.h"
-
-
-//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
- static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
-
-namespace __cxxabiv1
-{
-struct __cxa_eh_globals;
-extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
-}
-
-inline
-int getUncaughtExceptionCount()
-{
- return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
-}
+#include "type_traits.h"
//best of Zen, Loki and C++17
@@ -53,7 +37,7 @@ enum class ScopeGuardRunMode
//partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
template <typename F> inline
-void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
+void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
{
try { fun(); }
catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
@@ -61,18 +45,18 @@ void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeG
template <typename F> inline
-void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
+void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
{
- const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ const bool failed = std::uncaught_exceptions() > exeptionCountOld;
if (!failed)
fun(); //throw X
}
template <typename F> inline
-void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
+void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
{
- const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ const bool failed = std::uncaught_exceptions() > exeptionCountOld;
if (failed)
try { fun(); }
catch (...) { assert(false); }
@@ -93,7 +77,7 @@ public:
~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
{
if (!dismissed_)
- runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
+ runScopeGuardDestructor(fun_, exeptionCount_, std::integral_constant<ScopeGuardRunMode, runMode>());
}
void dismiss() { dismissed_ = true; }
@@ -103,7 +87,7 @@ private:
ScopeGuard& operator=(const ScopeGuard&) = delete;
F fun_;
- const int exeptionCount_ = getUncaughtExceptionCount();
+ const int exeptionCount_ = std::uncaught_exceptions();
bool dismissed_ = false;
};
diff --git a/zen/serialize.h b/zen/serialize.h
index 381d102a..16375cff 100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -21,7 +21,7 @@ namespace zen
--------------------------
|Binary Container Concept|
--------------------------
-binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<char>, std::string, Zbase<char>)
+binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<std::byte>, std::string, Zbase<char>)
*/
//binary container reference implementations
@@ -29,12 +29,12 @@ using Utf8String = Zbase<char>; //ref-counted + COW text stream + guaranteed per
class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?)
-class ByteArray //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
+class ByteArray //essentially a std::vector<std::byte> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
{
public:
- using value_type = std::vector<char>::value_type;
- using iterator = std::vector<char>::iterator;
- using const_iterator = std::vector<char>::const_iterator;
+ using value_type = std::vector<std::byte>::value_type;
+ using iterator = std::vector<std::byte>::iterator;
+ using const_iterator = std::vector<std::byte>::const_iterator;
iterator begin() { return buffer_->begin(); }
iterator end () { return buffer_->end (); }
@@ -49,7 +49,7 @@ public:
inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; }
private:
- std::shared_ptr<std::vector<char>> buffer_ { std::make_shared<std::vector<char>>() }; //always bound!
+ std::shared_ptr<std::vector<std::byte>> buffer_ = std::make_shared<std::vector<std::byte>>(); //always bound!
//perf: shared_ptr indirection irrelevant: less than 1% slower!
};
@@ -122,10 +122,11 @@ struct MemoryStreamIn
size_t read(void* buffer, size_t bytesToRead) //return "bytesToRead" bytes unless end of stream!
{
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ using Byte = typename BinContainer::value_type;
+ static_assert(sizeof(Byte) == 1);
const size_t bytesRead = std::min(bytesToRead, buffer_.size() - pos_);
auto itFirst = buffer_.begin() + pos_;
- std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(buffer));
+ std::copy(itFirst, itFirst + bytesRead, static_cast<Byte*>(buffer));
pos_ += bytesRead;
return bytesRead;
}
@@ -147,9 +148,11 @@ struct MemoryStreamOut
void write(const void* buffer, size_t bytesToWrite)
{
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ using Byte = typename BinContainer::value_type;
+ static_assert(sizeof(Byte) == 1);
buffer_.resize(buffer_.size() + bytesToWrite);
- std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.end() - bytesToWrite);
+ const auto it = static_cast<const Byte*>(buffer);
+ std::copy(it, it + bytesToWrite, buffer_.end() - bytesToWrite);
}
const BinContainer& ref() const { return buffer_; }
@@ -177,7 +180,7 @@ void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X
if (blockSize == 0)
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
- std::vector<char> buffer(blockSize);
+ std::vector<std::byte> buffer(blockSize);
for (;;)
{
const size_t bytesRead = streamIn.read(&buffer[0], blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
@@ -192,7 +195,7 @@ void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X
template <class BinContainer, class BufferedInputStream> inline
BinContainer bufferedLoad(BufferedInputStream& streamIn) //throw X
{
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ static_assert(sizeof(typename BinContainer::value_type) == 1); //expect: bytes
const size_t blockSize = streamIn.getBlockSize();
if (blockSize == 0)
@@ -221,7 +224,7 @@ void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len)
template <class N, class BufferedOutputStream> inline
void writeNumber(BufferedOutputStream& stream, const N& num)
{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!");
+ static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool>);
writeArray(stream, &num, sizeof(N));
}
@@ -249,7 +252,7 @@ void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw Un
template <class N, class BufferedInputStream> inline
N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
+ static_assert(IsArithmetic<N>::value || std::is_same_v<N, bool>);
N num = 0;
readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
return num;
diff --git a/zen/socket.h b/zen/socket.h
new file mode 100755
index 00000000..c92071e2
--- /dev/null
+++ b/zen/socket.h
@@ -0,0 +1,84 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SOCKET_H_23498325972583947678456437
+#define SOCKET_H_23498325972583947678456437
+
+#include <zen/zstring.h>
+#include "sys_error.h"
+ #include <unistd.h> //close
+ #include <sys/socket.h>
+ #include <netdb.h> //getaddrinfo
+
+
+namespace zen
+{
+#define THROW_LAST_SYS_ERROR_WSA(functionName) \
+ do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+
+
+//Winsock needs to be initialized before calling any of these functions! (WSAStartup/WSACleanup)
+
+class Socket //throw SysError
+{
+public:
+ Socket(const Zstring& server, const Zstring& serviceName) //throw SysError
+ {
+ ::addrinfo hints = {};
+ hints.ai_socktype = SOCK_STREAM; //we *do* care about this one!
+ hints.ai_flags = AI_ADDRCONFIG; //save a AAAA lookup on machines that can't use the returned data anyhow
+
+ ::addrinfo* servinfo = nullptr;
+ ZEN_ON_SCOPE_EXIT(if (servinfo) ::freeaddrinfo(servinfo));
+
+ const int rcGai = ::getaddrinfo(server.c_str(), serviceName.c_str(), &hints, &servinfo);
+ if (rcGai != 0)
+ throw SysError(formatSystemError(L"getaddrinfo", replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(rcGai)), utfTo<std::wstring>(::gai_strerror(rcGai))));
+ if (!servinfo)
+ throw SysError(L"getaddrinfo: empty server info");
+
+ auto getConnectedSocket = [&](const auto& /*::addrinfo*/ ai)
+ {
+ SocketType testSocket = ::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
+ if (testSocket == invalidSocket)
+ THROW_LAST_SYS_ERROR_WSA(L"socket");
+ ZEN_ON_SCOPE_FAIL(closeSocket(testSocket));
+
+ if (::connect(testSocket, ai.ai_addr, static_cast<int>(ai.ai_addrlen)) != 0)
+ THROW_LAST_SYS_ERROR_WSA(L"connect");
+
+ return testSocket;
+ };
+
+ Opt<SysError> firstError;
+ for (const auto* /*::addrinfo*/ si = servinfo; si; si = si->ai_next)
+ try
+ {
+ socket_ = getConnectedSocket(*si); //throw SysError; pass ownership
+ return;
+ }
+ catch (const SysError& e) { if (!firstError) firstError = e; }
+
+ throw* firstError; //list was not empty, so there must have been an error!
+ }
+
+ ~Socket() { closeSocket(socket_); }
+
+ using SocketType = int;
+ SocketType get() const { return socket_; }
+
+private:
+ Socket (const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ static const SocketType invalidSocket = -1;
+ static void closeSocket(SocketType s) { ::close(s); }
+
+ SocketType socket_ = invalidSocket;
+};
+}
+
+#endif //SOCKET_H_23498325972583947678456437
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index f09639e1..2ce2cf33 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -12,7 +12,7 @@
#include <vector>
#include <memory>
#include <algorithm>
-#include "type_tools.h"
+#include "type_traits.h"
#include "build_info.h"
@@ -54,10 +54,6 @@ template <class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
BidirectionalIterator2 first2, BidirectionalIterator2 last2);
-template <class InputIterator1, class InputIterator2>
-bool equal(InputIterator1 first1, InputIterator1 last1,
- InputIterator2 first2, InputIterator2 last2);
-
template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last);
template <class Num, class ByteIterator> Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last);
@@ -69,7 +65,7 @@ struct StringHash
{
const auto* strFirst = strBegin(str);
return hashBytes<size_t>(reinterpret_cast<const char*>(strFirst),
- reinterpret_cast<const char*>(strFirst + strLength(str)));
+ reinterpret_cast<const char*>(strFirst + strLength(str)));
}
};
@@ -181,35 +177,25 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bi
}
-template <class InputIterator1, class InputIterator2> inline
-bool equal(InputIterator1 first1, InputIterator1 last1,
- InputIterator2 first2, InputIterator2 last2)
-{
- return last1 - first1 == last2 - first2 && std::equal(first1, last1, first2);
-}
-
-
-
-
//FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
template <class Num, class ByteIterator> inline
-Num hashBytes(ByteIterator first, ByteIterator last)
+Num hashBytes(ByteIterator first, ByteIterator last)
{
- static_assert(std::is_integral<Num>::value, "");
- static_assert(sizeof(Num) == 4 || sizeof(Num) == 8, ""); //macOS: size_t is "unsigned long"
- const Num base = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL;
+ static_assert(IsInteger<Num>::value);
+ static_assert(sizeof(Num) == 4 || sizeof(Num) == 8); //macOS: size_t is "unsigned long"
+ constexpr Num base = sizeof(Num) == 4 ? 2166136261U : 14695981039346656037ULL;
- return hashBytesAppend(base, first, last);
+ return hashBytesAppend(base, first, last);
}
template <class Num, class ByteIterator> inline
Num hashBytesAppend(Num hashVal, ByteIterator first, ByteIterator last)
{
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
- const Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL;
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1);
+ constexpr Num prime = sizeof(Num) == 4 ? 16777619U : 1099511628211ULL;
- for (; first != last; ++first)
+ for (; first != last; ++first)
{
hashVal ^= static_cast<Num>(*first);
hashVal *= prime;
diff --git a/zen/string_base.h b/zen/string_base.h
index 2d043d4f..9632eba4 100755
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -27,9 +27,9 @@ Allocator Policy:
class AllocatorOptimalSpeed //exponential growth + min size
{
protected:
- //::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
- static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::free(ptr); }
+ //::operator new/delete show same performance characterisics like malloc()/free()!
+ static void* allocate(size_t size) { return ::operator new (size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::operator delete (ptr); }
static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); }
//- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length!
//- any growth rate should not exceed golden ratio: 1.618033989
@@ -39,8 +39,8 @@ protected:
class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string
{
protected:
- static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::free(ptr); }
+ static void* allocate(size_t size) { return ::operator new (size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::operator delete (ptr); }
static size_t calcCapacity(size_t length) { return length; }
};
@@ -114,7 +114,7 @@ private:
capacity(static_cast<uint32_t>(cap)) {}
uint32_t length;
- uint32_t capacity; //allocated size without null-termination
+ const uint32_t capacity; //allocated size without null-termination
};
static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
@@ -187,11 +187,15 @@ private:
{
Descriptor(size_t len, size_t cap) :
length (static_cast<uint32_t>(len)),
- capacity(static_cast<uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "The atomic type is always lock-free"
+ capacity(static_cast<uint32_t>(cap))
+ {
+ static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free"
+ //static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3)
+ }
std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
uint32_t length;
- uint32_t capacity; //allocated size without null-termination
+ const uint32_t capacity; //allocated size without null-termination
};
static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
@@ -234,8 +238,10 @@ public:
iterator begin();
iterator end ();
+
const_iterator begin () const { return rawStr_; }
const_iterator end () const { return rawStr_ + length(); }
+
const_iterator cbegin() const { return begin(); }
const_iterator cend () const { return end (); }
@@ -357,7 +363,7 @@ Zbase<Char, SP>::Zbase(Zbase<Char, SP>&& tmp) noexcept
template <class Char, template <class> class SP> inline
Zbase<Char, SP>::~Zbase()
{
- static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default
+ static_assert(noexcept(this->~Zbase())); //has exception spec of compiler-generated destructor by default
this->destroy(rawStr_); //rawStr_ may be nullptr; see move constructor!
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 7734b6f0..58cb6ea6 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -127,7 +127,7 @@ bool isWhiteSpace(wchar_t c)
template <class Char> inline
bool isDigit(Char c) //similar to implmenetation of std::isdigit()!
{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
return static_cast<Char>('0') <= c && c <= static_cast<Char>('9');
}
@@ -135,7 +135,7 @@ bool isDigit(Char c) //similar to implmenetation of std::isdigit()!
template <class Char> inline
bool isHexDigit(Char c)
{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
return (static_cast<Char>('0') <= c && c <= static_cast<Char>('9')) ||
(static_cast<Char>('A') <= c && c <= static_cast<Char>('F')) ||
(static_cast<Char>('a') <= c && c <= static_cast<Char>('f'));
@@ -145,7 +145,7 @@ bool isHexDigit(Char c)
template <class Char> inline
bool isAsciiAlpha(Char c)
{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
return (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) ||
(static_cast<Char>('a') <= c && c <= static_cast<Char>('z'));
}
@@ -209,7 +209,7 @@ template <class S, class T> inline bool strEqual (const S& lhs, const T& rhs
template <class S, class T> inline
bool contains(const S& str, const T& term)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t strLen = strLength(str);
const size_t termLen = strLength(term);
if (strLen < termLen)
@@ -227,7 +227,7 @@ bool contains(const S& str, const T& term)
template <class S, class T> inline
S afterLast(const S& str, const T& term, FailureReturnVal rv)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t termLen = strLength(term);
assert(termLen > 0);
@@ -248,7 +248,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv)
template <class S, class T> inline
S beforeLast(const S& str, const T& term, FailureReturnVal rv)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t termLen = strLength(term);
assert(termLen > 0);
@@ -268,7 +268,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv)
template <class S, class T> inline
S afterFirst(const S& str, const T& term, FailureReturnVal rv)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t termLen = strLength(term);
assert(termLen > 0);
@@ -289,7 +289,7 @@ S afterFirst(const S& str, const T& term, FailureReturnVal rv)
template <class S, class T> inline
S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t termLen = strLength(term);
assert(termLen > 0);
@@ -309,7 +309,7 @@ S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
template <class S, class T> inline
std::vector<S> split(const S& str, const T& delimiter, SplitType st)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const size_t delimLen = strLength(delimiter);
assert(delimLen > 0);
if (delimLen == 0)
@@ -346,18 +346,18 @@ ZEN_INIT_DETECT_MEMBER(append);
//either call operator+=(S(str, len)) or append(str, len)
template <class S, class InputIterator> inline
-typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); }
+std::enable_if_t<HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); }
template <class S, class InputIterator> inline
-typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); }
+std::enable_if_t<!HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); }
}
template <class S, class T, class U> inline
S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
+ static_assert(std::is_same_v<GetCharTypeT<T>, GetCharTypeT<U>>);
const size_t oldLen = strLength(oldTerm);
if (oldLen == 0)
return str;
@@ -427,7 +427,7 @@ void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
template <class S> inline
void trim(S& str, bool fromLeft, bool fromRight)
{
- using CharType = typename GetCharType<S>::Type;
+ using CharType = GetCharTypeT<S>;
trim(str, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
}
@@ -509,11 +509,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const
template <class S, class T, class Num> inline
S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- using CharType = typename GetCharType<S>::Type;
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
const int BUFFER_SIZE = 128;
- CharType buffer[BUFFER_SIZE]; //zero-initialize?
+ GetCharTypeT<S> buffer[BUFFER_SIZE]; //zero-initialize?
const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number);
return 0 < charsWritten && charsWritten < BUFFER_SIZE ? S(buffer, charsWritten) : S();
@@ -522,21 +521,19 @@ S printNumber(const T& format, const Num& number) //format a single number using
namespace impl
{
-enum NumberType
+enum class NumberType
{
- NUM_TYPE_SIGNED_INT,
- NUM_TYPE_UNSIGNED_INT,
- NUM_TYPE_FLOATING_POINT,
- NUM_TYPE_OTHER,
+ SIGNED_INT,
+ UNSIGNED_INT,
+ FLOATING_POINT,
+ OTHER,
};
template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
{
- using CharType = typename GetCharType<S>::Type;
-
- std::basic_ostringstream<CharType> ss;
+ std::basic_ostringstream<GetCharTypeT<S>> ss;
ss << number;
return copyStringTo<S>(ss.str());
}
@@ -546,9 +543,9 @@ template <class S, class Num> inline S floatToString(const Num& number, char )
template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); }
template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
- return floatToString<S>(number, typename GetCharType<S>::Type());
+ return floatToString<S>(number, GetCharTypeT<S>());
}
@@ -591,10 +588,9 @@ void formatPositiveInteger(Num n, OutputIterator& it)
template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::SIGNED_INT>)
{
- using CharType = typename GetCharType<S>::Type;
- CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize?
+ GetCharTypeT<S> buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize?
//it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
//required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1!
// <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41)
@@ -612,10 +608,9 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>)
template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
+S numberTo(const Num& number, std::integral_constant<NumberType, NumberType::UNSIGNED_INT>)
{
- using CharType = typename GetCharType<S>::Type;
- CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize?
+ GetCharTypeT<S> buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize?
//required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41)
auto it = std::end(buffer);
@@ -628,9 +623,9 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
//--------------------------------------------------------------------------------
template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::OTHER>) //default string to number conversion using streams: convenient, but SLOW
{
- using CharType = typename GetCharType<S>::Type;
+ using CharType = GetCharTypeT<S>;
Num number = 0;
std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number;
return number;
@@ -641,7 +636,7 @@ template <class Num> inline Num stringToFloat(const char* str) { return std::
template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); }
template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>)
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::FLOATING_POINT>)
{
return stringToFloat<Num>(strBegin(str));
}
@@ -649,7 +644,7 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>)
template <class Num, class S>
Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
{
- using CharType = typename GetCharType<S>::Type;
+ using CharType = GetCharTypeT<S>;
const CharType* first = strBegin(str);
const CharType* last = first + strLength(str);
@@ -687,7 +682,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i
template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>)
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::SIGNED_INT>)
{
bool hasMinusSign = false; //handle minus sign
const Num number = extractInteger<Num>(str, hasMinusSign);
@@ -696,7 +691,7 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>)
template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
+Num stringTo(const S& str, std::integral_constant<NumberType, NumberType::UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
{
bool hasMinusSign = false; //handle minus sign
const Num number = extractInteger<Num>(str, hasMinusSign);
@@ -713,11 +708,11 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversi
template <class S, class Num> inline
S numberTo(const Num& number)
{
- using TypeTag = Int2Type<
- IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
- IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
- IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
- impl::NUM_TYPE_OTHER>;
+ using TypeTag = std::integral_constant<impl::NumberType,
+ IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT :
+ IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT :
+ IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT :
+ impl::NumberType::OTHER>;
return impl::numberTo<S>(number, TypeTag());
}
@@ -726,11 +721,11 @@ S numberTo(const Num& number)
template <class Num, class S> inline
Num stringTo(const S& str)
{
- using TypeTag = Int2Type<
- IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
- IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
- IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
- impl::NUM_TYPE_OTHER>;
+ using TypeTag = std::integral_constant<impl::NumberType,
+ IsSignedInt <Num>::value ? impl::NumberType::SIGNED_INT :
+ IsUnsignedInt<Num>::value ? impl::NumberType::UNSIGNED_INT :
+ IsFloat <Num>::value ? impl::NumberType::FLOATING_POINT :
+ impl::NumberType::OTHER>;
return impl::stringTo<Num>(str, TypeTag());
}
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 805db46d..8187126d 100755
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -8,20 +8,20 @@
#define STRING_TRAITS_H_813274321443234
#include <cstring> //strlen
-#include "type_tools.h"
+#include "type_traits.h"
//uniform access to string-like types, both classes and character arrays
namespace zen
{
/*
-IsStringLike<>::value:
- IsStringLike<const wchar_t*>::value; //equals "true"
- IsStringLike<const int*> ::value; //equals "false"
+IsStringLikeV<>:
+ IsStringLikeV<const wchar_t*> //equals "true"
+ IsStringLikeV<const int*> //equals "false"
-GetCharType<>::Type:
- GetCharType<std::wstring>::Type //equals wchar_t
- GetCharType<wchar_t[5]> ::Type //equals wchar_t
+GetCharTypeT<>:
+ GetCharTypeT<std::wstring> //equals wchar_t
+ GetCharTypeT<wchar_t[5]> //equals wchar_t
strLength():
strLength(str); //equals str.length()
@@ -34,6 +34,7 @@ strBegin(): -> not null-terminated! -> may be nullptr if length is 0!
strBegin(array); //returns array
*/
+
//reference a sub-string for consumption by zen string_tools
template <class Char>
class StringRef
@@ -79,15 +80,14 @@ public:
};
-template <class S, bool isStringClass> struct GetCharTypeImpl : ResultType<NullType> {};
+template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
template <class S>
-struct GetCharTypeImpl<S, true> :
- ResultType<
- typename SelectIf<HasConversion<S, wchar_t>::value, wchar_t,
- typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type
- >::Type>
+struct GetCharTypeImpl<S, true>
{
+ using Type = std::conditional_t<HasConversion<S, wchar_t>::value, wchar_t,
+ /**/ std::conditional_t<HasConversion<S, char >::value, char, void>>;
+
//using Type = typename S::value_type;
/*DON'T use S::value_type:
1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*"
@@ -96,13 +96,13 @@ struct GetCharTypeImpl<S, true> :
*/
};
-template <> struct GetCharTypeImpl<char, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {};
+template <> struct GetCharTypeImpl<char, false> { using Type = char; };
+template <> struct GetCharTypeImpl<wchar_t, false> { using Type = wchar_t; };
-template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {};
-template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {};
+template <> struct GetCharTypeImpl<StringRef<char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> { using Type = wchar_t; };
+template <> struct GetCharTypeImpl<StringRef<const char >, false> { using Type = char; };
+template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> { using Type = wchar_t; };
ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
@@ -112,35 +112,42 @@ ZEN_INIT_DETECT_MEMBER(length); //
template <class S>
class StringTraits
{
- using NonRefType = typename RemoveRef <S >::Type;
- using NonConstType = typename RemoveConst <NonRefType >::Type;
- using NonArrayType = typename RemoveArray <NonConstType>::Type;
- using NonPtrType = typename RemovePointer<NonArrayType>::Type;
- using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const"
+ using CleanType = std::remove_cv_t<std::remove_reference_t<S>>; //std::remove_cvref requires C++20
+ using NonArrayType = std::remove_extent_t <CleanType>;
+ using NonPtrType = std::remove_pointer_t<NonArrayType>;
+ using UndecoratedType = std::remove_cv_t <NonPtrType>; //handle "const char* const"
public:
enum
{
- isStringClass = HasMemberType_value_type<NonConstType>::value &&
- HasMember_c_str <NonConstType>::value &&
- HasMember_length <NonConstType>::value
+ isStringClass = HasMemberType_value_type<CleanType>::value &&
+ HasMember_c_str <CleanType>::value &&
+ HasMember_length <CleanType>::value
};
using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type;
enum
{
- isStringLike = IsSameType<CharType, char>::value ||
- IsSameType<CharType, wchar_t>::value
+ isStringLike = std::is_same_v<CharType, char> ||
+ std::is_same_v<CharType, wchar_t>
};
};
}
template <class T>
-struct IsStringLike : StaticBool<impl::StringTraits<T>::isStringLike> {};
+struct IsStringLike : std::bool_constant<impl::StringTraits<T>::isStringLike> {};
template <class T>
-struct GetCharType : ResultType<typename impl::StringTraits<T>::CharType> {};
+struct GetCharType { using Type = typename impl::StringTraits<T>::CharType; };
+
+
+//template alias helpers:
+template<class T>
+constexpr bool IsStringLikeV = IsStringLike<T>::value;
+
+template<class T>
+using GetCharTypeT = typename GetCharType<T>::Type;
namespace impl
@@ -154,7 +161,7 @@ inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); }
template <class C> inline
size_t cStringLength(const C* str)
{
- static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, "");
+ static_assert(std::is_same_v<C, char> || std::is_same_v<C, wchar_t>);
size_t len = 0;
while (*str++ != 0)
++len;
@@ -162,8 +169,8 @@ size_t cStringLength(const C* str)
}
#endif
-template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline
-const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string"
+template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline
+const GetCharTypeT<S>* strBegin(const S& str) //SFINAE: T must be a "string"
{
return str.c_str();
}
@@ -179,7 +186,7 @@ inline const char* strBegin(const StringRef<const char >& ref) { return ref
inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); }
-template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline
+template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline
size_t strLength(const S& str) //SFINAE: T must be a "string"
{
return str.length();
@@ -198,9 +205,9 @@ inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length
template <class S> inline
-auto strBegin(S&& str) -> const typename GetCharType<S>::Type*
+auto strBegin(S&& str) -> const GetCharTypeT<S>*
{
- static_assert(IsStringLike<S>::value, "");
+ static_assert(IsStringLikeV<S>);
return impl::strBegin(std::forward<S>(str));
}
@@ -208,7 +215,7 @@ auto strBegin(S&& str) -> const typename GetCharType<S>::Type*
template <class S> inline
size_t strLength(S&& str)
{
- static_assert(IsStringLike<S>::value, "");
+ static_assert(IsStringLikeV<S>);
return impl::strLength(std::forward<S>(str));
}
}
diff --git a/zen/sys_error.h b/zen/sys_error.h
index c179ec8a..a087172f 100755
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -81,7 +81,6 @@ std::wstring formatSystemError(const std::wstring& functionName, long long lastE
inline
std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec)
{
- //static_assert(sizeof(ec) == sizeof(int), "");
//const std::wstring errorCode = printNumber<std::wstring>(L"0x%08x", static_cast<int>(ec));
const std::wstring errorCode = numberTo<std::wstring>(ec);
diff --git a/zen/thread.cpp b/zen/thread.cpp
new file mode 100755
index 00000000..8016d4a9
--- /dev/null
+++ b/zen/thread.cpp
@@ -0,0 +1,55 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "thread.h"
+ #include <sys/prctl.h>
+ #include <unistd.h>
+ #include <sys/syscall.h>
+
+using namespace zen;
+
+
+
+
+void zen::setCurrentThreadName(const char* threadName)
+{
+ ::prctl(PR_SET_NAME, threadName, 0, 0, 0);
+
+}
+
+
+namespace
+{
+uint64_t getThreadIdNative()
+{
+ const pid_t tid = ::syscall(SYS_gettid); //no-fail
+ //"Invalid thread and process IDs": https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503
+ //if (tid == 0) -> not sure this holds on Linux, too!
+ // throw std::runtime_error(std::string(__FILE__) + "[" + numberTo<std::string>(__LINE__) + "] Failed to get thread ID.");
+ static_assert(sizeof(uint64_t) >= sizeof(tid));
+ return tid;
+}
+
+
+struct InitMainThreadIdOnStartup
+{
+ InitMainThreadIdOnStartup() { getMainThreadId(); }
+} startupInitMainThreadId;
+}
+
+
+uint64_t zen::getThreadId()
+{
+ thread_local const uint64_t tid = getThreadIdNative(); //buffer to get predictable perf characteristics
+ return tid;
+}
+
+
+uint64_t zen::getMainThreadId()
+{
+ static const uint64_t mainThreadId = getThreadId();
+ return mainThreadId;
+}
diff --git a/zen/thread.h b/zen/thread.h
index ed61e06b..ee36f305 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -10,9 +10,9 @@
#include <thread>
#include <future>
#include "scope_guard.h"
-#include "type_traits.h"
+#include "ring_buffer.h"
#include "optional.h"
- #include <sys/prctl.h>
+#include "string_tools.h"
namespace zen
@@ -23,8 +23,8 @@ class InterruptibleThread
{
public:
InterruptibleThread() {}
- InterruptibleThread (InterruptibleThread&& tmp) = default;
- InterruptibleThread& operator=(InterruptibleThread&& tmp) = default;
+ InterruptibleThread (InterruptibleThread&&) noexcept = default;
+ InterruptibleThread& operator=(InterruptibleThread&&) noexcept = default;
template <class Function>
InterruptibleThread(Function&& f);
@@ -46,7 +46,7 @@ public:
private:
std::thread stdThread_;
- std::shared_ptr<InterruptionStatus> intStatus_{ std::make_shared<InterruptionStatus>() };
+ std::shared_ptr<InterruptionStatus> intStatus_ = std::make_shared<InterruptionStatus>();
std::future<void> threadCompleted_;
};
@@ -62,7 +62,9 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //th
void setCurrentThreadName(const char* threadName);
uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+uint64_t getMainThreadId();
+inline bool runningMainThread() { return getThreadId() == getMainThreadId(); }
//------------------------------------------------------------------------------------------
/*
@@ -118,6 +120,7 @@ class Protected
public:
Protected() {}
Protected(const T& value) : value_(value) {}
+ //Protected( T&& tmp ) : value_(std::move(tmp)) {} <- wait until needed
template <class Function>
auto access(Function fun) //-> decltype(fun(std::declval<T&>()))
@@ -140,53 +143,101 @@ template <class Function>
class ThreadGroup
{
public:
- ThreadGroup(size_t threadCount, const std::string& groupName)
- {
- for (size_t i = 0; i < threadCount; ++i)
- worker_.emplace_back([this, groupName, i, threadCount]
- {
- setCurrentThreadName((groupName + "[" + numberTo<std::string>(i + 1) + "/" + numberTo<std::string>(threadCount) + "]").c_str());
- for (;;)
- getNextWorkItem()(); //throw ThreadInterruption
- });
- }
+ ThreadGroup(size_t threadCountMax, const std::string& groupName) : threadCountMax_(threadCountMax), groupName_(groupName)
+ { if (threadCountMax == 0) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); }
+
~ThreadGroup()
{
for (InterruptibleThread& w : worker_) w.interrupt(); //interrupt all first, then join
- for (InterruptibleThread& w : worker_) w.join();
+ for (InterruptibleThread& w : worker_) detach_ ? w.detach() : w.join();
}
+ ThreadGroup(ThreadGroup&& tmp) noexcept :
+ worker_ (std::move(tmp.worker_)),
+ workLoad_ (std::move(tmp.workLoad_)),
+ detach_ (tmp.detach_),
+ threadCountMax_(tmp.threadCountMax_),
+ groupName_ (std::move(tmp.groupName_)) { tmp.worker_.clear(); /*just in case: make sure destructor is no-op!*/ }
+
+ ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!!
+
//context of controlling thread, non-blocking:
void run(Function&& wi)
{
- assert(!worker_.empty());
+ size_t tasksPending = 0;
{
- std::lock_guard<std::mutex> dummy(lockWork_);
- workItems_.push_back(std::move(wi));
+ std::lock_guard<std::mutex> dummy(workLoad_->lock);
+ workLoad_->tasks.push_back(std::move(wi));
+ tasksPending = ++(workLoad_->tasksPending);
}
- conditionNewWork_.notify_all();
+ workLoad_->conditionNewTask.notify_all();
+
+ if (worker_.size() < std::min(tasksPending, threadCountMax_))
+ addWorkerThread();
}
+ //context of controlling thread, blocking:
+ void wait()
+ {
+ std::unique_lock<std::mutex> dummy(workLoad_->lock);
+ workLoad_->conditionTasksDone.wait(dummy, [&tasksPending = workLoad_->tasksPending] { return tasksPending == 0; });
+ }
+
+ void detach() { detach_ = true; } //not expected to also interrupt!
+
private:
ThreadGroup (const ThreadGroup&) = delete;
ThreadGroup& operator=(const ThreadGroup&) = delete;
- //context of worker threads, blocking:
- Function getNextWorkItem() //throw ThreadInterruption
+ void addWorkerThread()
{
- std::unique_lock<std::mutex> dummy(lockWork_);
+ std::string threadName = groupName_ + '[' + numberTo<std::string>(worker_.size() + 1) + '/' + numberTo<std::string>(threadCountMax_) + ']';
+
+ worker_.emplace_back([wl = workLoad_, threadName = std::move(threadName)] //don't capture "this"! consider detach() and swap()
+ {
+ setCurrentThreadName(threadName.c_str());
+
+ std::unique_lock<std::mutex> dummy(wl->lock);
+ for (;;)
+ {
+ interruptibleWait(wl->conditionNewTask, dummy, [&tasks = wl->tasks] { return !tasks.empty(); }); //throw ThreadInterruption
+
+ Function task = std::move(wl->tasks. front()); //noexcept thanks to move
+ /**/ wl->tasks.pop_front(); //
+ dummy.unlock();
- interruptibleWait(conditionNewWork_, dummy, [this] { return !workItems_.empty(); }); //throw ThreadInterruption
- warn_static("implement FIFO!?")
+ task();
- Function wi = std::move(workItems_. back()); //
- /**/ workItems_.pop_back(); //noexcept thanks to move
- return wi; //
+ dummy.lock();
+ if (--(wl->tasksPending) == 0)
+ wl->conditionTasksDone.notify_all(); //too difficult to notify outside the lock
+ }
+ });
}
+
+ void swap(ThreadGroup& other)
+ {
+ std::swap(worker_, other.worker_);
+ std::swap(workLoad_, other.workLoad_);
+ std::swap(detach_, other.detach_);
+ std::swap(threadCountMax_, other.threadCountMax_);
+ std::swap(groupName_, other.groupName_);
+ }
+
+ struct WorkLoad
+ {
+ std::mutex lock;
+ RingBuffer<Function> tasks; //FIFO! :)
+ size_t tasksPending = 0;
+ std::condition_variable conditionNewTask;
+ std::condition_variable conditionTasksDone;
+ };
+
std::vector<InterruptibleThread> worker_;
- std::mutex lockWork_;
- std::vector<Function> workItems_;
- std::condition_variable conditionNewWork_;
+ std::shared_ptr<WorkLoad> workLoad_ = std::make_shared<WorkLoad>();
+ bool detach_ = false;
+ size_t threadCountMax_;
+ std::string groupName_;
};
@@ -201,7 +252,7 @@ private:
namespace impl
{
template <class Function> inline
-auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
+auto runAsync(Function&& fun, std::true_type /*copy-constructible*/)
{
using ResultType = decltype(fun());
@@ -214,11 +265,11 @@ auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
template <class Function> inline
-auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
+auto runAsync(Function&& fun, std::false_type /*copy-constructible*/)
{
//support move-only function objects!
auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun));
- return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType());
+ return runAsync([sharedFun] { return (*sharedFun)(); }, std::true_type());
}
}
@@ -226,7 +277,7 @@ auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
template <class Function> inline
auto runAsync(Function&& fun)
{
- return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>());
+ return impl::runAsync(std::forward<Function>(fun), std::is_copy_constructible<Function>());
}
@@ -308,14 +359,6 @@ Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal
//------------------------------------------------------------------------------------------
-//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
-#if defined __GNUC__ || defined __clang__
- #define ZEN_THREAD_LOCAL_SPECIFIER __thread
-#else
- #error "Game over!"
-#endif
-
-
class ThreadInterruption {};
@@ -354,7 +397,7 @@ public:
ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr));
//"interrupted_" is not protected by cv's mutex => signal may get lost!!! e.g. after condition was checked but before the wait begins
- //=> add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
+ //=> add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); }))
;
@@ -393,7 +436,8 @@ namespace impl
inline
InterruptionStatus*& refThreadLocalInterruptionStatus()
{
- static ZEN_THREAD_LOCAL_SPECIFIER InterruptionStatus* threadLocalInterruptionStatus = nullptr;
+ //thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
+ thread_local InterruptionStatus* threadLocalInterruptionStatus = nullptr;
return threadLocalInterruptionStatus;
}
}
@@ -457,26 +501,6 @@ InterruptibleThread::InterruptibleThread(Function&& f)
inline
void InterruptibleThread::interrupt() { intStatus_->interrupt(); }
-
-
-
-
-inline
-void setCurrentThreadName(const char* threadName)
-{
- ::prctl(PR_SET_NAME, threadName, 0, 0, 0);
-
-}
-
-
-inline
-uint64_t getThreadId()
-{
- //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
- static_assert(sizeof(uint64_t) >= sizeof(void*), "");
- return reinterpret_cast<uint64_t>(static_cast<void*>(&errno));
-
-}
}
#endif //THREAD_H_7896323423432235246427
diff --git a/zen/time.h b/zen/time.h
index 723614ef..b06d3d15 100755
--- a/zen/time.h
+++ b/zen/time.h
@@ -217,12 +217,11 @@ struct PredefinedFormatTag {};
template <class String, class String2> inline
String formatTime(const String2& format, const TimeComp& tc, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure
{
- using CharType = typename GetCharType<String>::Type;
std::tm ctc = toClibTimeComponents(tc);
std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday
//note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent
- CharType buffer[256] = {};
+ GetCharTypeT<String> buffer[256] = {};
const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc);
return String(buffer, charsWritten);
}
@@ -231,8 +230,7 @@ String formatTime(const String2& format, const TimeComp& tc, UserDefinedFormatTa
template <class String, class FormatType> inline
String formatTime(FormatType, const TimeComp& tc, PredefinedFormatTag)
{
- using CharType = typename GetCharType<String>::Type;
- return formatTime<String>(GetFormat<FormatType>().format(CharType()), tc, UserDefinedFormatTag());
+ return formatTime<String>(GetFormat<FormatType>().format(GetCharTypeT<String>()), tc, UserDefinedFormatTag());
}
}
@@ -306,13 +304,13 @@ String formatTime(const String2& format, const TimeComp& tc)
if (tc == TimeComp()) //failure code from getLocalTime()
return String();
- using FormatTag = typename SelectIf<
- IsSameType<String2, FormatDateTag >::value ||
- IsSameType<String2, FormatTimeTag >::value ||
- IsSameType<String2, FormatDateTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTag >::value ||
- IsSameType<String2, FormatIsoTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type;
+ using FormatTag = std::conditional_t<
+ std::is_same_v<String2, FormatDateTag > ||
+ std::is_same_v<String2, FormatTimeTag > ||
+ std::is_same_v<String2, FormatDateTimeTag > ||
+ std::is_same_v<String2, FormatIsoDateTag > ||
+ std::is_same_v<String2, FormatIsoTimeTag > ||
+ std::is_same_v<String2, FormatIsoDateTimeTag>, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>;
return impl::formatTime<String>(format, tc, FormatTag());
}
@@ -323,8 +321,8 @@ namespace impl
template <class String, class String2>
TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTag)
{
- using CharType = typename GetCharType<String>::Type;
- static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, "");
+ using CharType = GetCharTypeT<String>;
+ static_assert(std::is_same_v<CharType, GetCharTypeT<String2>>);
const CharType* itStr = strBegin(str);
const CharType* const strLast = itStr + strLength(str);
@@ -429,8 +427,7 @@ TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTa
template <class FormatType, class String> inline
TimeComp parseTime(FormatType, const String& str, PredefinedFormatTag)
{
- using CharType = typename GetCharType<String>::Type;
- return parseTime(GetFormat<FormatType>().format(CharType()), str, UserDefinedFormatTag());
+ return parseTime(GetFormat<FormatType>().format(GetCharTypeT<String>()), str, UserDefinedFormatTag());
}
}
@@ -438,10 +435,10 @@ TimeComp parseTime(FormatType, const String& str, PredefinedFormatTag)
template <class String, class String2> inline
TimeComp parseTime(const String& format, const String2& str)
{
- using FormatTag = typename SelectIf<
- IsSameType<String, FormatIsoDateTag >::value ||
- IsSameType<String, FormatIsoTimeTag >::value ||
- IsSameType<String, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type;
+ using FormatTag = std::conditional_t<
+ std::is_same_v<String, FormatIsoDateTag > ||
+ std::is_same_v<String, FormatIsoTimeTag > ||
+ std::is_same_v<String, FormatIsoDateTimeTag>, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>;
return impl::parseTime(format, str, FormatTag());
}
diff --git a/zen/type_tools.h b/zen/type_tools.h
deleted file mode 100755
index a705d9bd..00000000
--- a/zen/type_tools.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TYPE_TOOLS_H_45237590734254545
-#define TYPE_TOOLS_H_45237590734254545
-
-#include "type_traits.h"
-
-
-namespace zen
-{
-//########## Strawman Classes ##########################
-struct NullType {}; //:= no type here
-
-//########## Type Mapping ##############################
-template <int n>
-struct Int2Type {};
-//------------------------------------------------------
-template <class T>
-struct Type2Type {};
-
-//########## Control Structures ########################
-template <bool flag, class T, class U>
-struct SelectIf : ResultType<T> {};
-
-template <class T, class U>
-struct SelectIf<false, T, U> : ResultType<U> {};
-//------------------------------------------------------
-template <class T, class U>
-struct IsSameType : FalseType {};
-
-template <class T>
-struct IsSameType<T, T> : TrueType {};
-
-//------------------------------------------------------
-template <bool, class T = void>
-struct EnableIf {};
-
-template <class T>
-struct EnableIf<true, T> : ResultType<T> {};
-//########## Type Cleanup ##############################
-template <class T>
-struct RemoveRef : ResultType<T> {};
-
-template <class T>
-struct RemoveRef<T&> : ResultType<T> {};
-
-template <class T>
-struct RemoveRef<T&&> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemoveConst : ResultType<T> {};
-
-template <class T>
-struct RemoveConst<const T> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemovePointer : ResultType<T> {};
-
-template <class T>
-struct RemovePointer<T*> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemoveArray : ResultType<T> {};
-
-template <class T, int N>
-struct RemoveArray<T[N]> : ResultType<T> {};
-
-//########## Sorting ##############################
-/*
-Generate a descending binary predicate at compile time!
-
-Usage:
- static const bool ascending = ...
- makeSortDirection(old binary predicate, Int2Type<ascending>()) -> new binary predicate
-
-or directly;
- makeDescending(old binary predicate) -> new binary predicate
-*/
-
-template <class Predicate>
-struct LessDescending
-{
- LessDescending(Predicate lessThan) : lessThan_(lessThan) {}
- template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); }
-private:
- Predicate lessThan_;
-};
-
-template <class Predicate> inline
-/**/ Predicate makeSortDirection(Predicate pred, Int2Type<true>) { return pred; }
-
-template <class Predicate> inline
-LessDescending<Predicate> makeSortDirection(Predicate pred, Int2Type<false>) { return pred; }
-
-template <class Predicate> inline
-LessDescending<Predicate> makeDescending(Predicate pred) { return pred; }
-}
-
-#endif //TYPE_TOOLS_H_45237590734254545
diff --git a/zen/type_traits.h b/zen/type_traits.h
index 83a74d1e..2d4e7a97 100755
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -7,41 +7,42 @@
#ifndef TYPE_TRAITS_H_3425628658765467
#define TYPE_TRAITS_H_3425628658765467
-#include <type_traits> //all we need is std::is_class!!
+#include <type_traits>
+#include "legacy_compiler.h"
+//http://en.cppreference.com/w/cpp/header/type_traits
namespace zen
{
-//################# TMP compile time return values: "inherit to return compile-time result" ##############
-template <int i>
-struct StaticInt
+template<class T, class...>
+struct GetFirstOf
{
- enum { value = i };
+ using Type = T;
};
+template<class... T> using GetFirstOfT = typename GetFirstOf<T...>::Type;
-template <bool b>
-struct StaticBool : StaticInt<b> {};
-
-using TrueType = StaticBool<true>;
-using FalseType = StaticBool<false>;
-
-template <class EnumType, EnumType val>
-struct StaticEnum
+template <class F>
+class FunctionReturnType
{
- static const EnumType value = val;
-};
-//---------------------------------------------------------
-template <class T>
-struct ResultType
-{
- using Type = T;
+ template <class R, class... Args> static R dummyFun(R(*)(Args...));
+public:
+ using Type = decltype(dummyFun(F()));
};
+template<class F> using FunctionReturnTypeT = typename FunctionReturnType<F>::Type;
-template<class T, class...>
-struct GetFirstOf
+//=============================================================================
+
+template<class T, size_t N>
+constexpr size_t arraySize(T (&)[N]) { return N; }
+
+template<class S, class T, size_t N>
+constexpr S arrayAccumulate(T (&arr)[N])
{
- using Type = T;
-};
+ S sum = 0;
+ for (size_t i = 0; i < N; ++i)
+ sum += arr[i];
+ return sum;
+}
//Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/
template<class T> inline auto makeSigned (T t) { return static_cast<std::make_signed_t <T>>(t); }
@@ -50,16 +51,19 @@ template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_u
//################# Built-in Types ########################
//Example: "IsSignedInt<int>::value" evaluates to "true"
+//unfortunate standardized nonsense: std::is_integral<> includes bool, char, wchar_t! => roll our own:
template <class T> struct IsUnsignedInt;
template <class T> struct IsSignedInt;
-template <class T> struct IsFloat;
-template <class T> struct IsInteger; //IsSignedInt or IsUnsignedInt
-template <class T> struct IsArithmetic; //IsInteger or IsFloat
+template <class T> using IsFloat = std::is_floating_point<T>;
+template <class T> using IsInteger = std::bool_constant<IsUnsignedInt<T>::value || IsSignedInt<T>::value>;
+template <class T> using IsArithmetic = std::bool_constant<IsInteger <T>::value || IsFloat <T>::value>;
+
//remaining non-arithmetic types: bool, char, wchar_t
+
//optional: specialize new types like:
-//template <> struct IsUnsignedInt<UInt64> : TrueType {};
+//template <> struct IsUnsignedInt<UInt64> : std::true_type {};
//################# Class Members ########################
@@ -80,13 +84,29 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat
2. HasMemberType_value_type<T>::value -> use as boolean
*/
+//########## Sorting ##############################
+/*
+Generate a descending binary predicate at compile time!
+Usage:
+ static const bool ascending = ...
+ makeSortDirection(old binary predicate, std::bool_constant<ascending>()) -> new binary predicate
+*/
+template <class Predicate>
+struct LessDescending
+{
+ LessDescending(Predicate lessThan) : lessThan_(lessThan) {}
+ template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); }
+private:
+ Predicate lessThan_;
+};
+template <class Predicate> inline
+/**/ Predicate makeSortDirection(Predicate pred, std::true_type) { return pred; }
-
-
-
+template <class Predicate> inline
+LessDescending<Predicate> makeSortDirection(Predicate pred, std::false_type) { return pred; }
@@ -95,43 +115,23 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat
//################ implementation ######################
-#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {};
-
template <class T>
-struct IsUnsignedInt : FalseType {};
+struct IsUnsignedInt : std::false_type {};
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - same type as unsigned __int64 in VS2010
-//------------------------------------------------------
+template <> struct IsUnsignedInt<unsigned char > : std::true_type {};
+template <> struct IsUnsignedInt<unsigned short int > : std::true_type {};
+template <> struct IsUnsignedInt<unsigned int > : std::true_type {};
+template <> struct IsUnsignedInt<unsigned long int > : std::true_type {};
+template <> struct IsUnsignedInt<unsigned long long int> : std::true_type {};
template <class T>
-struct IsSignedInt : FalseType {};
-
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, long int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type as __int64 in VS2010
-//------------------------------------------------------
+struct IsSignedInt : std::false_type {};
-template <class T>
-struct IsFloat : FalseType {};
-
-ZEN_SPECIALIZE_TRAIT(IsFloat, float);
-ZEN_SPECIALIZE_TRAIT(IsFloat, double);
-ZEN_SPECIALIZE_TRAIT(IsFloat, long double);
-//------------------------------------------------------
-
-#undef ZEN_SPECIALIZE_TRAIT
-
-template <class T>
-struct IsInteger : StaticBool<IsUnsignedInt<T>::value || IsSignedInt<T>::value> {};
-
-template <class T>
-struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
+template <> struct IsSignedInt<signed char > : std::true_type {};
+template <> struct IsSignedInt<short int > : std::true_type {};
+template <> struct IsSignedInt<int > : std::true_type {};
+template <> struct IsSignedInt<long int > : std::true_type {};
+template <> struct IsSignedInt<long long int> : std::true_type {};
//####################################################################
#define ZEN_INIT_DETECT_MEMBER(NAME) \
@@ -145,6 +145,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
\
template <typename U, U t> \
class Helper {}; \
+ \
struct Fallback { int NAME; }; \
\
template <class U> \
@@ -156,11 +157,11 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \
}; \
\
- template<class T> \
- struct HasMemberImpl_##NAME<false, T> : FalseType {}; \
+ template<class T> \
+ struct HasMemberImpl_##NAME<false, T> : std::false_type {}; \
\
- template<typename T> \
- struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {};
+ template<typename T> \
+ struct HasMember_##NAME : std::bool_constant<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {};
//####################################################################
diff --git a/zen/utf.h b/zen/utf.h
index f8ee91d5..48269416 100755
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -53,6 +53,8 @@ const CodePoint TRAIL_SURROGATE_MAX = 0xdfff;
const CodePoint REPLACEMENT_CHAR = 0xfffd;
const CodePoint CODE_POINT_MAX = 0x10ffff;
+static_assert(LEAD_SURROGATE + TRAIL_SURROGATE + TRAIL_SURROGATE_MAX + REPLACEMENT_CHAR + CODE_POINT_MAX == 1348603);
+
template <class Function> inline
void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16
@@ -245,14 +247,14 @@ private:
//----------------------------------------------------------------------------------------------------------------
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<4>) { writeOutput(cp); } //other OS: UTF32-wchar_t
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, std::integral_constant<int, 4>) { writeOutput(cp); } //other OS: UTF32-wchar_t
template <class CharType, class Function> inline
void codePointToUtf(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a CharType
{
- return codePointToUtf(cp, writeOutput, Int2Type<sizeof(CharType)>());
+ return codePointToUtf(cp, writeOutput, std::integral_constant<int, sizeof(CharType)>());
}
//----------------------------------------------------------------------------------------------------------------
@@ -311,7 +313,7 @@ bool isValidUtf(const UtfString& str)
{
using namespace impl;
- UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ UtfDecoder<GetCharTypeT<UtfString>> decoder(strBegin(str), strLength(str));
while (Opt<CodePoint> cp = decoder.getNext())
if (*cp == REPLACEMENT_CHAR)
return false;
@@ -324,7 +326,7 @@ template <class UtfString> inline
size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding)
{
size_t uniLen = 0;
- impl::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ impl::UtfDecoder<GetCharTypeT<UtfString>> decoder(strBegin(str), strLength(str));
while (decoder.getNext())
++uniLen;
return uniLen;
@@ -336,7 +338,7 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u
{
assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str));
using namespace impl;
- using CharType = typename GetCharType<UtfString>::Type;
+ using CharType = GetCharTypeT<UtfString>;
UtfString output;
if (uniPosFirst >= uniPosLast) //optimize for empty range
return output;
@@ -357,11 +359,11 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u
namespace impl
{
template <class TargetString, class SourceString> inline
-TargetString utfTo(const SourceString& str, FalseType)
+TargetString utfTo(const SourceString& str, std::false_type)
{
- using CharSrc = typename GetCharType<SourceString>::Type;
- using CharTrg = typename GetCharType<TargetString>::Type;
- static_assert(sizeof(CharSrc) != sizeof(CharTrg), "no UTF-conversion needed");
+ using CharSrc = GetCharTypeT<SourceString>;
+ using CharTrg = GetCharTypeT<TargetString>;
+ static_assert(sizeof(CharSrc) != sizeof(CharTrg));
TargetString output;
@@ -374,14 +376,14 @@ TargetString utfTo(const SourceString& str, FalseType)
template <class TargetString, class SourceString> inline
-TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<TargetString>(str); }
+TargetString utfTo(const SourceString& str, std::true_type) { return copyStringTo<TargetString>(str); }
}
template <class TargetString, class SourceString> inline
TargetString utfTo(const SourceString& str)
{
- return impl::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>());
+ return impl::utfTo<TargetString>(str, std::bool_constant<sizeof(GetCharTypeT<SourceString>) == sizeof(GetCharTypeT<TargetString>)>());
}
}
diff --git a/zen/warn_static.h b/zen/warn_static.h
index e4931c08..6f0a2691 100755
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -14,13 +14,11 @@ Usage:
warn_static("my message")
*/
-#if defined __GNUC__
-#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y
-#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y)
+#define ZEN_STATIC_WARNING_STRINGIZE(NUM) #NUM
-#define warn_static(TXT) \
- typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
- enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
+#if defined __GNUC__ //Clang also defines __GNUC__!
+#define warn_static(MSG) \
+ _Pragma(ZEN_STATIC_WARNING_STRINGIZE(GCC warning MSG))
#endif
#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index afa62c93..1fe31eaf 100755
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -54,8 +54,8 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh
if (!cpL || !cpR)
return static_cast<int>(!cpR) - static_cast<int>(!cpL);
-//support unit-testing on Windows: CodePoint is truncated to wchar_t
-static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint), "");
+ //support unit-testing on Windows: CodePoint is truncated to wchar_t
+ static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint));
const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper()
const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale
diff --git a/zen/zstring.h b/zen/zstring.h
index 5a6ecbdd..cb19318c 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -18,6 +18,9 @@
//a high-performance string for interfacing with native OS APIs in multithreaded contexts
using Zstring = zen::Zbase<Zchar>;
+ //for special UI-contexts: guaranteed exponential growth + ref-counting
+using Zstringw = zen::Zbase<wchar_t>;
+
//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES
struct CmpFilePath
@@ -131,8 +134,8 @@ template <class S, class T, class U> inline
S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm)
{
using namespace zen;
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
+ static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>);
+ static_assert(std::is_same_v<GetCharTypeT<T>, GetCharTypeT<U>>);
const size_t oldLen = strLength(oldTerm);
if (oldLen == 0)
return str;
diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h
index 3a18ea73..11795107 100755
--- a/zenXml/zenxml/cvrt_struc.h
+++ b/zenXml/zenxml/cvrt_struc.h
@@ -56,14 +56,13 @@ ZEN_INIT_DETECT_MEMBER(insert) //
}
template <typename T>
-struct IsStlContainer :
- StaticBool<
- impl_2384343::HasMemberType_value_type <T>::value&&
- impl_2384343::HasMemberType_iterator <T>::value&&
- impl_2384343::HasMemberType_const_iterator<T>::value&&
- impl_2384343::HasMember_begin <T>::value&&
- impl_2384343::HasMember_end <T>::value&&
- impl_2384343::HasMember_insert <T>::value> {};
+using IsStlContainer = std::bool_constant<
+ impl_2384343::HasMemberType_value_type <T>::value &&
+ impl_2384343::HasMemberType_iterator <T>::value &&
+ impl_2384343::HasMemberType_const_iterator<T>::value &&
+ impl_2384343::HasMember_begin <T>::value &&
+ impl_2384343::HasMember_end <T>::value &&
+ impl_2384343::HasMember_insert <T>::value>;
namespace impl_2384343
@@ -76,29 +75,28 @@ ZEN_INIT_DETECT_MEMBER(second) //
}
template <typename T>
-struct IsStlPair :
- StaticBool<
- impl_2384343::HasMemberType_first_type <T>::value&&
- impl_2384343::HasMemberType_second_type<T>::value&&
- impl_2384343::HasMember_first <T>::value&&
- impl_2384343::HasMember_second <T>::value> {};
+using IsStlPair = std::bool_constant<
+ impl_2384343::HasMemberType_first_type <T>::value &&
+ impl_2384343::HasMemberType_second_type<T>::value &&
+ impl_2384343::HasMember_first <T>::value &&
+ impl_2384343::HasMember_second <T>::value>;
//######################################################################################
//Conversion from arbitrary types to an XML element
-enum ValueType
+enum class ValueType
{
- VALUE_TYPE_STL_CONTAINER,
- VALUE_TYPE_STL_PAIR,
- VALUE_TYPE_OTHER,
+ STL_CONTAINER,
+ STL_PAIR,
+ OTHER,
};
template <class T>
-struct GetValueType : StaticEnum<ValueType,
- GetTextType<T>::value != TEXT_TYPE_OTHER ? VALUE_TYPE_OTHER : //some string classes are also STL containers, so check this first
- IsStlContainer<T>::value ? VALUE_TYPE_STL_CONTAINER :
- IsStlPair<T>::value ? VALUE_TYPE_STL_PAIR :
- VALUE_TYPE_OTHER> {};
+using GetValueType = std::integral_constant<ValueType,
+ GetTextType <T>::value != TEXT_TYPE_OTHER ? ValueType::OTHER : //some string classes are also STL containers, so check this first
+ IsStlContainer<T>::value ? ValueType::STL_CONTAINER :
+ IsStlPair <T>::value ? ValueType::STL_PAIR :
+ ValueType::OTHER>;
template <class T, ValueType type>
@@ -113,7 +111,7 @@ struct ConvertElement;
//partial specialization: handle conversion for all STL-container types!
template <class T>
-struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER>
+struct ConvertElement<T, ValueType::STL_CONTAINER>
{
void writeStruc(const T& value, XmlElement& output) const
{
@@ -145,7 +143,7 @@ struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER>
//partial specialization: handle conversion for std::pair
template <class T>
-struct ConvertElement<T, VALUE_TYPE_STL_PAIR>
+struct ConvertElement<T, ValueType::STL_PAIR>
{
void writeStruc(const T& value, XmlElement& output) const
{
@@ -173,7 +171,7 @@ struct ConvertElement<T, VALUE_TYPE_STL_PAIR>
//partial specialization: not a pure structured type, try text conversion (thereby respect user specializations of writeText()/readText())
template <class T>
-struct ConvertElement<T, VALUE_TYPE_OTHER>
+struct ConvertElement<T, ValueType::OTHER>
{
void writeStruc(const T& value, XmlElement& output) const
{
diff --git a/zenXml/zenxml/cvrt_text.h b/zenXml/zenxml/cvrt_text.h
index 17444861..32a961b2 100755
--- a/zenXml/zenxml/cvrt_text.h
+++ b/zenXml/zenxml/cvrt_text.h
@@ -112,10 +112,10 @@ enum TextType
};
template <class T>
-struct GetTextType : StaticEnum<TextType,
- IsSameType<T, bool>::value ? TEXT_TYPE_BOOL :
- IsStringLike<T>::value ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
- IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : //
+struct GetTextType : std::integral_constant<TextType,
+ std::is_same_v<T, bool> ? TEXT_TYPE_BOOL :
+ IsStringLikeV<T> ? TEXT_TYPE_STRING : //string before number to correctly handle char/wchar_t -> this was an issue with Loki only!
+ IsArithmetic<T>::value ? TEXT_TYPE_NUMBER : //
TEXT_TYPE_OTHER> {};
//######################################################################################
@@ -186,7 +186,7 @@ template <class T>
struct ConvertText<T, TEXT_TYPE_OTHER>
{
//###########################################################################################################################################
- static_assert(sizeof(T) == -1, "");
+ static_assert(sizeof(T) == -1);
/*
ATTENTION: The data type T is yet unknown to the zen::Xml framework!
diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h
index 15700ee2..566af330 100755
--- a/zenXml/zenxml/dom.h
+++ b/zenXml/zenxml/dom.h
@@ -10,7 +10,6 @@
#include <string>
#include <list>
#include <map>
-#include <zen/fixed_list.h>
#include "cvrt_text.h" //"readText/writeText"
@@ -144,9 +143,15 @@ public:
template < class IterTy, //underlying iterator type
class T, //target object type
class AccessPolicy > //access policy: see AccessPtrMap
- class PtrIter : public std::iterator<std::input_iterator_tag, T>, private AccessPolicy //get rid of shared_ptr indirection
+ class PtrIter : private AccessPolicy //get rid of shared_ptr indirection
{
public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+
PtrIter(IterTy it) : it_(it) {}
PtrIter(const PtrIter& other) : it_(other.it_) {}
PtrIter& operator++() { ++it_; return *this; }
@@ -191,8 +196,8 @@ public:
T& objectRef(const IterTy& it) const { return *it; }
};
- using ChildIter = PtrIter<FixedList<XmlElement>::iterator, XmlElement, AccessListElement>;
- using ChildIterConst = PtrIter<FixedList<XmlElement>::const_iterator, const XmlElement, AccessListElement>;
+ using ChildIter = PtrIter<std::list<XmlElement>::iterator, XmlElement, AccessListElement>;
+ using ChildIterConst = PtrIter<std::list<XmlElement>::const_iterator, const XmlElement, AccessListElement>;
///Access all child elements sequentially via STL iterators.
/**
@@ -257,7 +262,7 @@ private:
std::list<Attribute> attributes_; //attributes in order of creation
std::map<std::string, std::list<Attribute>::iterator> attributesSorted_; //alternate view: sorted by attribute name
- FixedList<XmlElement> childElements_; //child elements in order of creation
+ std::list<XmlElement> childElements_; //child elements in order of creation
std::multimap<std::string, XmlElement*> childElementsSorted_; //alternate view: sorted by element name
XmlElement* parent_ = nullptr;
};
bgstack15