summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRodeo <[email protected]>2013-10-11 13:17:17 +0000
committerRodeo <[email protected]>2013-10-11 13:17:17 +0000
commit58bd6b80db26fc71a101c0dbdf4633deea5c0c34 (patch)
treeaafdf5dcb4e1ce061fdbd44a7b6be9f14cf6efb3
parent54fde2698cac2a49a0ce786ec04a13fd7360e529 (diff)
QSV: use encode-only path when we have CPU filters enabled and CopyFrame is unavailable.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@5831 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--libhb/qsv_common.c8
-rw-r--r--libhb/qsv_common.h1
-rw-r--r--libhb/work.c47
3 files changed, 54 insertions, 2 deletions
diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c
index d191fadf1..704a6e86a 100644
--- a/libhb/qsv_common.c
+++ b/libhb/qsv_common.c
@@ -93,6 +93,10 @@ int hb_qsv_info_init()
hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_EXTBRC;
}
+ if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7))
+ {
+ hb_qsv_info->capabilities |= HB_QSV_CAP_COPYFRAME;
+ }
if (hb_get_cpu_platform() == HB_CPU_PLATFORM_INTEL_HSW)
{
if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 6))
@@ -114,6 +118,10 @@ int hb_qsv_info_init()
hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID;
}
+ if (HB_CHECK_MFX_VERSION(qsv_software_version, 1, 7))
+ {
+ hb_qsv_info->capabilities |= HB_QSV_CAP_COPYFRAME;
+ }
}
// note: we pass a pointer to MFXInit but it never gets modified
diff --git a/libhb/qsv_common.h b/libhb/qsv_common.h
index 083065be6..ab9ca9024 100644
--- a/libhb/qsv_common.h
+++ b/libhb/qsv_common.h
@@ -36,6 +36,7 @@ typedef struct hb_qsv_info_s
#define HB_QSV_CAP_OPTION2_MBBRC (1 << 3) // mfxExtCodingOption2: MBBRC
#define HB_QSV_CAP_OPTION2_LOOKAHEAD (1 << 4) // mfxExtCodingOption2: LookAhead
#define HB_QSV_CAP_OPTION2_TRELLIS (1 << 5) // mfxExtCodingOption2: Trellis
+#define HB_QSV_CAP_COPYFRAME (1 << 6) // mfxCoreInterface: CopyFrame
// TODO: add available decoders, filters, encoders,
// maximum decode and encode resolution, etc.
diff --git a/libhb/work.c b/libhb/work.c
index a5c3190d1..bd5b66529 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -700,6 +700,48 @@ static void do_job(hb_job_t *job)
#ifdef USE_QSV
/*
+ * XXX: mfxCoreInterface's CopyFrame doesn't work in old drivers, and our
+ * workaround is really slow. If we have validated CPU-based filters in
+ * the list and we can't use CopyFrame, disable QSV decoding until a
+ * better solution is implemented.
+ */
+ if (!(hb_qsv_info->capabilities & HB_QSV_CAP_COPYFRAME))
+ {
+ if (job->list_filter != NULL)
+ {
+ for (i = 0;
+ i < hb_list_count(job->list_filter) && hb_qsv_decode_is_enabled(job);
+ i++)
+ {
+ hb_filter_object_t *filter = hb_list_item(job->list_filter, i);
+ switch (filter->id)
+ {
+ // validated, CPU-based filters
+ case HB_FILTER_ROTATE:
+ case HB_FILTER_RENDER_SUB:
+ hb_log("do_job: QSV: CopyFrame unavailable, using encode-only path");
+ job->qsv.decode = 0;
+ break;
+
+ // CPU-based deinterlace (validated)
+ case HB_FILTER_DEINTERLACE:
+ if (filter->settings != NULL &&
+ strcasecmp(filter->settings, "qsv") != 0)
+ {
+ hb_log("do_job: QSV: CopyFrame unavailable, using encode-only path");
+ job->qsv.decode = 0;
+ }
+ break;
+
+ // other filters will be removed
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /*
* When QSV is used for decoding, not all CPU-based filters are supported,
* so we need to do a little extra setup here.
*/
@@ -744,7 +786,8 @@ static void do_job(hb_job_t *job)
// pick VPP or CPU deinterlace depending on settings
case HB_FILTER_DEINTERLACE:
- if (filter->settings == NULL || !strcmp(filter->settings, "qsv"))
+ if (filter->settings == NULL ||
+ strcasecmp(filter->settings, "qsv") == 0)
{
// deinterlacing via VPP filter
vpp_settings[6] = 1;
@@ -766,7 +809,7 @@ static void do_job(hb_job_t *job)
// finally, drop all unsupported filters
default:
- hb_log("do_job: full QSV path, removing unsupported filter '%s'",
+ hb_log("do_job: QSV: full path, removing unsupported filter '%s'",
filter->name);
hb_list_rem(job->list_filter, filter);
hb_filter_close(&filter);
href='#n382'>382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429
/**
 * Author: Sven Gothel <sgothel@jausoft.com>
 * Copyright (c) 2022 Gothel Software e.K.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef JAU_FILE_UTIL_HPP_
#define JAU_FILE_UTIL_HPP_

#include <jau/fraction_type.hpp>
#include <jau/functional.hpp>
#include <memory>
#include <string>

extern "C" {
    // #include <sys/stat.h>
    // for ::mode_t posix protection bits
    #include <sys/types.h>
}

namespace jau::fs {

    /** @defgroup FileUtils File Utilities
     *  File types and functionality.
     *
     *  @{
     */

    /**
     * Return the current working directory or empty on failure.
     */
    std::string get_cwd() noexcept;

    /** Change working directory */
    bool chdir(const std::string& path) noexcept;

    /**
     * Returns the absolute path of given `relpath` if existing,
     * otherwise an empty string.
     * @param relpath a path, might be relative
     */
    std::string absolute(const std::string_view& relpath) noexcept;

    /**
     * Return stripped last component from given path separated by `/`, excluding the trailing separator `/`.
     *
     * If no directory separator `/` is contained, return `.`.
     *
     * If only the root path `/` is given, return `/`.
     *
     * @param path given path
     * @return leading directory name w/o slash or `.`
     */
    std::string dirname(const std::string_view& path) noexcept;

    /**
     * Return stripped leading directory components from given path separated by `/`.
     *
     * If only the root path `/` is given, return `/`.
     *
     * @param path given path
     * @return last non-slash component or `.`
     */
    std::string basename(const std::string_view& path) noexcept;

    /** Returns true if first character is `/` or - in case of Windows - `\\`. */
    bool isAbsolute(const std::string_view& path) noexcept;

    /**
     * Representing a directory item split into dirname() and basename().
     */
    class dir_item {
        private:
            std::string dirname_;
            std::string basename_;
            bool empty_;

            struct backed_string_view {
                std::string backing;
                std::string_view view;

                backed_string_view() noexcept
                : backing(), view(backing) {}

                backed_string_view(const std::string& backing_, const std::string_view& view_) noexcept
                : backing(backing_),
                    view(backing_.size() > 0 ? ((std::string_view)backing).substr(view_.data() - backing_.data(), view_.size()) : view_) {}

                backed_string_view(const std::string_view& view_) noexcept
                : backing(), view(view_) {}

#if 0
                backed_string_view(const backed_string_view& o) noexcept
                : backing(o.backing),
                    view( o.is_backed() ? ((std::string_view)backing).substr(o.view.data() - o.backing.data(), o.view.size()) : o.view)
                {}
#else
                /** Reason: Inefficient, removing the whole purpose of this class reducing std::string duplication. */
                backed_string_view(const backed_string_view& o) noexcept = delete;
#endif

#if 0
                backed_string_view(backed_string_view&& o) noexcept
                : backing( std::move(o.backing) ),
                    view( std::move(o.view) )
                {
                    fprintf(stderr, "backed_string_view move_ctor %s\n", to_string(true).c_str());
                }
#else
                /** Reason: clang - for some reason - does not move a std::string, but copies it */
                backed_string_view(backed_string_view&& o) noexcept = delete;
#endif

                bool is_backed() const noexcept { return backing.size() > 0; }

                void backup() noexcept {
                    backing = std::string(view);
                    view = backing;
                }
                void backup(const std::string& orig) noexcept {
                    backing = orig;
                    view = backing;
                }
                void backup(const std::string_view& orig) noexcept {
                    backing = std::string(orig);
                    view = backing;
                }
                void backup_and_append(const std::string& orig, const std::string& appendix) noexcept {
                    backing = orig;
                    backing.append(appendix);
                    view = backing;
                }
                void backup_and_append(const std::string_view& orig, const std::string& appendix) noexcept {
                    backing = std::string(orig);
                    backing.append(appendix);
                    view = backing;
                }

                std::string to_string(const bool destailed = false) const noexcept {
                    if (destailed) {
                        return "[backing '" + backing + "', view '" + std::string(view) + "']";
                    }
                    return std::string(view);
                }
            };
            static std::unique_ptr<backed_string_view> reduce(const std::string_view& path_) noexcept;

            dir_item(std::unique_ptr<backed_string_view> cleanpath) noexcept;

        public:
            /** Empty item w/ `.` set for both, dirname and basename. empty() will return true; */
            dir_item() noexcept;

            /**
             * Create a dir_item where path is split into dirname and basename after `.` and `..` has been reduced.
             *
             * empty() will return true if given path_ is empty
             *
             * @param path_ the raw path
             */
            dir_item(const std::string_view& path_) noexcept;

            /**
             * Create a dir_item with already cleaned dirname and basename
             * without any further processing nor validation.
             *
             * empty() will return true if both, given dirname_ and basename_ is empty
             *
             * @param dirname__
             * @param basename__
             * @see reduce()
             * @see jau::fs::dirname()
             * @see jau::fs::basename()
             */
            dir_item(std::string dirname__, std::string basename__) noexcept;

            /** Returns the dirname, shall not be empty and denotes `.` for current working director. */
            const std::string& dirname() const noexcept { return dirname_; }

            /** Return the basename, shall not be empty nor contain a dirname. */
            const std::string& basename() const noexcept { return basename_; }

            /**
             * Returns a full unix path representation combining dirname() and basename().
             */
            std::string path() const noexcept;

            /**
             * Returns true if bot, dirname() and basename() refer to `.`, e.g.. default ctor.
             */
            bool empty() const noexcept { return empty_; }

            bool operator==(const dir_item& rhs) const noexcept {
                return dirname_ == rhs.dirname_ && basename_ == rhs.basename_;
            }

            bool operator!=(const dir_item& rhs) const noexcept {
                return !(*this == rhs);
            }

            /**
             * Returns a comprehensive string representation of this item
             */
            std::string to_string() const noexcept;
    };

    /**
     * Generic file type and POSIX protection mode bits as used in file_stats, touch(), mkdir() etc.
     *
     * The POSIX protection mode bits reside in the lower 16-bits and are bit-wise POSIX compliant
     * while the file type bits reside in the upper 16-bits and are platform agnostic.
     *
     * This `enum class` type fulfills `C++ named requirements: BitmaskType`.
     *
     * @see file_stats
     * @see file_stats::mode()
     */
    enum class fmode_t : uint32_t {
        /** No mode bit set */
        none = 0,

        /** Protection bit: POSIX S_ISUID */
        set_uid = 04000,
        /** Protection bit: POSIX S_ISGID */
        set_gid = 02000,
        /** Protection bit: POSIX S_ISVTX */
        sticky = 01000,
        /** Protection bit: POSIX S_ISUID | S_ISGID | S_ISVTX */
        ugs_set = 07000,

        /** Protection bit: POSIX S_IRUSR */
        read_usr = 00400,
        /** Protection bit: POSIX S_IWUSR */
        write_usr = 00200,
        /** Protection bit: POSIX S_IXUSR */
        exec_usr = 00100,
        /** Protection bit: POSIX S_IRWXU */
        rwx_usr = 00700,

        /** Protection bit: POSIX S_IRGRP */
        read_grp = 00040,
        /** Protection bit: POSIX S_IWGRP */
        write_grp = 00020,
        /** Protection bit: POSIX S_IXGRP */
        exec_grp = 00010,
        /** Protection bit: POSIX S_IRWXG */
        rwx_grp = 00070,

        /** Protection bit: POSIX S_IROTH */
        read_oth = 00004,
        /** Protection bit: POSIX S_IWOTH */
        write_oth = 00002,
        /** Protection bit: POSIX S_IXOTH */
        exec_oth = 00001,
        /** Protection bit: POSIX S_IRWXO */
        rwx_oth = 00007,

        /** Protection bit: POSIX S_IRWXU | S_IRWXG | S_IRWXO or rwx_usr | rwx_grp | rwx_oth */
        rwx_all = 00777,

        /** Default directory protection bit: Safe default: POSIX S_IRWXU | S_IRGRP | S_IXGRP or rwx_usr | read_grp | exec_grp */
        def_dir_prot = 00750,

        /** Default file protection bit: Safe default: POSIX S_IRUSR | S_IWUSR | S_IRGRP or read_usr | write_usr | read_grp */
        def_file_prot = 00640,

        /** 12 bit protection bit mask 07777 for rwx_all | set_uid | set_gid | sticky . */
        protection_mask = 0b00000000000000000000111111111111,

        /** Type: Entity is a socket, might be in combination with link. */
        sock = 0b00000000000000000001000000000000,
        /** Type: Entity is a block device, might be in combination with link. */
        blk = 0b00000000000000000010000000000000,
        /** Type: Entity is a character device, might be in combination with link. */
        chr = 0b00000000000000000100000000000000,
        /** Type: Entity is a fifo/pipe, might be in combination with link. */
        fifo = 0b00000000000000001000000000000000,
        /** Type: Entity is a directory, might be in combination with link. */
        dir = 0b00000000000000010000000000000000,
        /** Type: Entity is a file, might be in combination with link. */
        file = 0b00000000000000100000000000000000,
        /** Type: Entity is a symbolic link, might be in combination with file or dir, fifo, chr, blk or sock. */
        link = 0b00000000000001000000000000000000,
        /** Type: Entity gives no access to user, exclusive bit. */
        no_access = 0b00100000000000000000000000000000,
        /** Type: Entity does not exist, exclusive bit. */
        not_existing = 0b01000000000000000000000000000000,
        /** Type mask for sock | blk | chr | fifo | dir | file | link | no_access | not_existing. */
        type_mask = 0b01100000000001111111000000000000,
    };
    constexpr uint32_t number(const fmode_t rhs) noexcept {
        return static_cast<uint32_t>(rhs);
    }
    constexpr fmode_t operator~(const fmode_t rhs) noexcept {
        return static_cast<fmode_t>(~number(rhs));
    }
    constexpr fmode_t operator^(const fmode_t lhs, const fmode_t rhs) noexcept {
        return static_cast<fmode_t>(number(lhs) ^ number(rhs));
    }
    constexpr fmode_t operator|(const fmode_t lhs, const fmode_t rhs) noexcept {
        return static_cast<fmode_t>(number(lhs) | number(rhs));
    }
    constexpr fmode_t operator&(const fmode_t lhs, const fmode_t rhs) noexcept {
        return static_cast<fmode_t>(number(lhs) & number(rhs));
    }
    constexpr fmode_t& operator|=(fmode_t& lhs, const fmode_t rhs) noexcept {
        lhs = static_cast<fmode_t>(number(lhs) | number(rhs));
        return lhs;
    }
    constexpr fmode_t& operator&=(fmode_t& lhs, const fmode_t rhs) noexcept {
        lhs = static_cast<fmode_t>(number(lhs) & number(rhs));
        return lhs;
    }
    constexpr fmode_t& operator^=(fmode_t& lhs, const fmode_t rhs) noexcept {
        lhs = static_cast<fmode_t>(number(lhs) ^ number(rhs));
        return lhs;
    }
    constexpr bool operator==(const fmode_t lhs, const fmode_t rhs) noexcept {
        return number(lhs) == number(rhs);
    }
    constexpr bool operator!=(const fmode_t lhs, const fmode_t rhs) noexcept {
        return !(lhs == rhs);
    }
    constexpr bool is_set(const fmode_t mask, const fmode_t bits) noexcept {
        return bits == (mask & bits);
    }
    /**
     * Return the string representation of fmode_t
     * @param mask the fmode_t to convert
     * @param show_rwx if true, return verbose POSIX protection bit string representation using `rwx` for user, group and others. Otherwise simply show the octal representation (default)
     * @return the string representation.
     */
    std::string to_string(const fmode_t mask, const bool show_rwx = false) noexcept;

    /** Returns the POSIX protection bits: rwx_all | set_uid | set_gid | sticky, i.e. fmode_t masked with fmode_t::protection_mask. */
    constexpr ::mode_t posix_protection_bits(const fmode_t mask) noexcept { return static_cast<::mode_t>(mask & fmode_t::protection_mask); }

    /**
     * Returns platform dependent named file descriptor of given file descriptor, if supported.
     *
     * Implementation returns (`%d` stands for integer):
     * - `/dev/fd/%d` (GNU/Linux, FreeBSD, ..)
     *
     * Following standard POSIX mappings exist
     * - fd 0, `/dev/fd/0`, `/dev/stdin`
     * - fd 1, `/dev/fd/1`, `/dev/stdout`
     * - fd 2, `/dev/fd/2`, `/dev/stderr`
     * - fd [0-99], `/dev/fd/[0-99]`
     *
     * Currently implementation always returns above pattern,
     * not handling the target OS differences.
     *
     * @param fd file descriptor.
     * @return the named file descriptor or an empty string if fd < 0 or not supported by OS.
     *
     * @see jau::fs::from_named_fd()
     * @see jau::fs::file_stats:has_fd()
     */
    std::string to_named_fd(const int fd) noexcept;

    /**
     * Returns the file descriptor from the given named file descriptor.
     *
     * Detected named file descriptors are (`%d` stands for integer)
     * - `/dev/fd/%d` (GNU/Linux, FreeBSD, ..)
     * - `/proc/self/fd/%d` (GNU/Linux)
     *
     * @param named_fd the named file descriptor
     * @return file descriptor or -1 if invalid or not supported by OS.
     *
     * @see jau::fs::to_named_fd()
     * @see jau::fs::file_stats:has_fd()
     */
    int from_named_fd(const std::string& named_fd) noexcept;

    /**
     * Platform agnostic representation of POSIX ::lstat() and ::stat()
     * for a given pathname.
     *
     * Implementation follows the symbolic link, i.e. first opens
     * the given pathname with ::lstat() and if identifying as a symbolic link
     * opens it via ::stat() to retrieve the actual properties like size, time and ownership.
     *
     * Implementation supports named file descriptor, see is_fd().
     *
     * On `GNU/Linux` implementation uses ::statx().
     */
    class file_stats {
        public:
            /**
             * Field identifier which bit-mask indicates field_t fields
             */
            enum class field_t : uint32_t {
                /** No mode bit set */
                none = 0,
                /** File type mode bits */
                type = 0b0000000000000001,
                /** POSIX file protection mode bits */
                mode = 0b0000000000000010,
                nlink = 0b0000000000000100,
                uid = 0b0000000000001000,
                gid = 0b0000000000010000,
                atime = 0b0000000000100000,
                mtime = 0b0000000001000000,
                ctime = 0b0000000010000000,
                ino = 0b0000000100000000,
                size = 0b0000001000000000,
                blocks = 0b0000010000000000,
                btime = 0b0000100000000000,
                fd = 0b0001000000000000
            };

            typedef uint32_t uid_t;
            typedef uint32_t gid_t;

        private:
            field_t has_fields_;

            dir_item item_;

            std::shared_ptr<std::string> link_target_path_;  // stored link-target path this symbolic-link points to if is_link(), otherwise nullptr.
            std::shared_ptr<file_stats> link_target_;        // link-target this symbolic-link points to if is_link(), otherwise nullptr.

            fmode_t mode_;
            int fd_;
            uid_t uid_;
            gid_t gid_;
            uint64_t size_;
            fraction_timespec btime_;  // Birth or creation time
            fraction_timespec atime_;  // Last access
            fraction_timespec ctime_;  // Last meta-status change
            fraction_timespec mtime_;  // Last modification

            int errno_res_;

            /** Private class only for private make_shared(). */
            class ctor_cookie {
                friend file_stats;
                uint16_t rec_level;
                ctor_cookie(const uint16_t recursion_level_) { rec_level = recursion_level_; }
            };

        public:
            /** Instantiate an empty file_stats with fmode_t::not_existing set. */
            file_stats() noexcept;

            /** Private ctor for private make_shared<file_stats>() intended for friends. */
            file_stats(const ctor_cookie& cc, int dirfd, const dir_item& item, const bool dirfd_is_item_dirname) noexcept;

            /**
             * Instantiates a file_stats for the given `path`.
             *
             * The dir_item will be constructed without parent_dir
             *
             * @param path the path to produce stats for
             */
            file_stats(const std::string& path) noexcept;

            /**
             * Instantiates a file_stats for the given `path`.
             *
             * The dir_item will be constructed without parent_dir
             *
             * @param dirfd file descriptor of given dir_item item's directory, dir_item::dirname(), or AT_FDCWD for the current working directory of the calling process
             * @param path the path to produce stats for
             */
            file_stats(const int dirfd, const std::string& path) noexcept;

            /**
             * Instantiates a file_stats for the given dir_item.
             *
             * @param item the dir_item to produce stats for
             */
            file_stats(const dir_item& item) noexcept;

            /**
             * Instantiates a file_stats for the given dir_item.
             *
             * @param dirfd file descriptor of given dir_item item's directory, dir_item::dirname(), or AT_FDCWD for the current working directory of the calling process
             * @param item the dir_item to produce stats for
             * @param dirfd_is_item_dirname if true, dir_item::basename() is relative to dirfd (default), otherwise full dir_item::path() is relative to dirfd.
             */
            file_stats(const int dirfd, const dir_item& item, const bool dirfd_is_item_dirname = true) noexcept;

            /**
             * Instantiates a file_stats for the given `fd` file descriptor.
             *
             * @param fd file descriptor of an opened file
             */
            file_stats(const int fd) noexcept;

            /**
             * Returns the dir_item.
             *
             * In case this instance is created by following a symbolic link instance,
             * it represents the resolved path relative to the used symbolic link's dirname.
             *
             * @see is_link()
             * @see path()
             */
            const dir_item& item() const noexcept { return item_; }

            /**
             * Returns the unix path representation.
             *
             * In case this instance is created by following a symbolic link instance,
             * it represents the resolved path relative to the used symbolic link's dirname.
             *
             * @see is_link()
             * @see item()
             */
            std::string path() const noexcept { return item_.path(); }

            /**
             * Returns the stored link-target path this symbolic-link points to if instance is a symbolic-link, otherwise nullptr.
             *
             * @see is_link()
             * @see link_target()
             * @see final_target()
             */
            const std::shared_ptr<std::string>& link_target_path() const noexcept { return link_target_path_; }

            /**
             * Returns the link-target this symbolic-link points to if instance is a symbolic-link, otherwise nullptr.
             *
             * nullptr is also returned for an erroneous symbolic-links, i.e. non-existing link-targets or recursive loop-errors.
             *
             * @see is_link()
             * @see link_target_path()
             * @see final_target()
             */
            const std::shared_ptr<file_stats>& link_target() const noexcept { return link_target_; }

            /**
             * Returns the final target element, either a pointer to this instance if not a symbolic-link
             * or the final link-target a symbolic-link (chain) points to.
             *
             * @param link_count optional size_t pointer to store the number of symbolic links leading to the final target, excluding the final instance. 0 indicates no symbolic-link;
             *
             * @see is_link()
             * @see link_target_path()
             * @see link_target()
             */
            const file_stats* final_target(size_t* link_count = nullptr) const noexcept;

            /** Returns true if the given field_t fields were retrieved, otherwise false. */
            bool has(const field_t fields) const noexcept;

            /** Returns the retrieved field_t fields. */
            constexpr field_t fields() const noexcept { return has_fields_; }

            /** Returns the fmode_t, file type and mode. */
            fmode_t mode() const noexcept { return mode_; }

            /** Returns the POSIX protection bit portion of fmode_t, i.e. mode() & fmode_t::protection_mask. */
            fmode_t prot_mode() const noexcept { return mode_ & fmode_t::protection_mask; }

            /** Returns the type bit portion of fmode_t, i.e. mode() & fmode_t::type_mask. */
            fmode_t type_mode() const noexcept { return mode_ & fmode_t::type_mask; }

            /**
             * Returns the file descriptor if has_fd(), otherwise -1 for no file descriptor.
             *
             * @see has_fd()
             */
            int fd() const noexcept { return fd_; }

            /** Returns the user id, owning the element. */
            uid_t uid() const noexcept { return uid_; }

            /** Returns the group id, owning the element. */
            gid_t gid() const noexcept { return gid_; }

            /**
             * Returns the size in bytes of this element if is_file(), otherwise zero.
             *
             * If the element also is_link(), the linked target size is returned.
             */
            uint64_t size() const noexcept { return size_; }

            /** Returns the birth time of this element since Unix Epoch, i.e. its creation time. */
            const fraction_timespec& btime() const noexcept { return btime_; }
            /** Returns the last access time of this element since Unix Epoch. */
            const fraction_timespec& atime() const noexcept { return atime_; }
            /** Returns the last status change time of this element since Unix Epoch. */
            const fraction_timespec& ctime() const noexcept { return ctime_; }
            /** Returns the last modification time of this element since Unix Epoch. */
            const fraction_timespec& mtime() const noexcept { return mtime_; }

            /** Returns the `errno` value occurred to produce this instance, or zero for no error. */
            constexpr int errno_res() const noexcept { return errno_res_; }

            /** Returns true if no error occurred */
            constexpr bool ok() const noexcept { return 0 == errno_res_; }

            /**
             * Returns true if entity has a file descriptor.
             *
             * @see fd()
             * @see jau::fs::from_named_fd()
             * @see jau::fs::to_named_fd()
             */
            constexpr bool has_fd() const noexcept { return 0 <= fd_; }

            /** Returns true if entity is a socket, might be in combination with is_link().  */
            constexpr bool is_socket() const noexcept { return is_set(mode_, fmode_t::sock); }

            /** Returns true if entity is a block device, might be in combination with is_link().  */
            constexpr bool is_block() const noexcept { return is_set(mode_, fmode_t::blk); }

            /** Returns true if entity is a character device, might be in combination with is_link().  */
            constexpr bool is_char() const noexcept { return is_set(mode_, fmode_t::chr); }

            /** Returns true if entity is a fifo/pipe, might be in combination with is_link().  */
            constexpr bool is_fifo() const noexcept { return is_set(mode_, fmode_t::fifo); }

            /** Returns true if entity is a directory, might be in combination with is_link().  */
            constexpr bool is_dir() const noexcept { return is_set(mode_, fmode_t::dir); }

            /** Returns true if entity is a file, might be in combination with is_link().  */
            constexpr bool is_file() const noexcept { return is_set(mode_, fmode_t::file); }

            /** Returns true if entity is a symbolic link, might be in combination with is_file(), is_dir(), is_fifo(), is_char(), is_block(), is_socket(). */
            constexpr bool is_link() const noexcept { return is_set(mode_, fmode_t::link); }

            /** Returns true if entity gives no access to user, exclusive bit. */
            constexpr bool has_access() const noexcept { return !is_set(mode_, fmode_t::no_access); }

            /** Returns true if entity does not exist, exclusive bit. */
            constexpr bool exists() const noexcept { return !is_set(mode_, fmode_t::not_existing); }

            bool operator==(const file_stats& rhs) const noexcept;

            bool operator!=(const file_stats& rhs) const noexcept {
                return !(*this == rhs);
            }

            /**
             * Returns a comprehensive string representation of this element
             */
            std::string to_string() const noexcept;
    };
    constexpr uint32_t number(const file_stats::field_t rhs) noexcept {
        return static_cast<uint32_t>(rhs);
    }
    constexpr file_stats::field_t operator~(const file_stats::field_t rhs) noexcept {
        return static_cast<file_stats::field_t>(~number(rhs));
    }
    constexpr file_stats::field_t operator^(const file_stats::field_t lhs, const file_stats::field_t rhs) noexcept {
        return static_cast<file_stats::field_t>(number(lhs) ^ number(rhs));
    }
    constexpr file_stats::field_t operator|(const file_stats::field_t lhs, const file_stats::field_t rhs) noexcept {
        return static_cast<file_stats::field_t>(number(lhs) | number(rhs));
    }
    constexpr file_stats::field_t operator&(const file_stats::field_t lhs, const file_stats::field_t rhs) noexcept {
        return static_cast<file_stats::field_t>(number(lhs) & number(rhs));
    }
    constexpr file_stats::field_t& operator|=(file_stats::field_t& lhs, const file_stats::field_t rhs) noexcept {
        lhs = static_cast<file_stats::field_t>(number(lhs) | number(rhs));
        return lhs;
    }
    constexpr file_stats::field_t& operator&=(file_stats::field_t& lhs, const file_stats::field_t rhs) noexcept {
        lhs = static_cast<file_stats::field_t>(number(lhs) & number(rhs));
        return lhs;
    }
    constexpr file_stats::field_t& operator^=(file_stats::field_t& lhs, const file_stats::field_t rhs) noexcept {
        lhs = static_cast<file_stats::field_t>(number(lhs) ^ number(rhs));
        return lhs;
    }
    constexpr bool operator==(const file_stats::field_t lhs, const file_stats::field_t rhs) noexcept {
        return number(lhs) == number(rhs);
    }
    constexpr bool operator!=(const file_stats::field_t lhs, const file_stats::field_t rhs) noexcept {
        return !(lhs == rhs);
    }
    constexpr bool is_set(const file_stats::field_t mask, const file_stats::field_t bits) noexcept {
        return bits == (mask & bits);
    }
    std::string to_string(const file_stats::field_t mask) noexcept;

    /**
     * Create directory
     * @param path full path to new directory
     * @param mode fmode_t POSIX protection bits used, defaults to jau::fs::fmode_t::def_dir_prot
     * @param verbose defaults to false
     * @return true if successful, otherwise false
     */
    bool mkdir(const std::string& path, const fmode_t mode = jau::fs::fmode_t::def_dir_prot, const bool verbose = false) noexcept;

    /**
     * Touch the file with given atime and mtime and create file if not existing yet.
     * @param path full path to file
     * @param atime new access time
     * @param mtime new modification time
     * @param mode fmode_t POSIX protection bits used, defaults to jau::fs::fmode_t::def_file_prot
     * @return true if successful, otherwise false
     */
    bool touch(const std::string& path, const jau::fraction_timespec& atime, const jau::fraction_timespec& mtime,
               const fmode_t mode = jau::fs::fmode_t::def_file_prot) noexcept;

    /**
     * Touch the file with current time and create file if not existing yet.
     * @param path full path to file
     * @param mode fmode_t POSIX protection bits used, defaults to jau::fs::fmode_t::def_file_prot
     * @return true if successful, otherwise false
     */
    bool touch(const std::string& path, const fmode_t mode = jau::fs::fmode_t::def_file_prot) noexcept;

    /**
     * `void consume_dir_item(const dir_item& item)`
     */
    typedef jau::function<void(const dir_item&)> consume_dir_item;

    /**
     * Returns a list of directory elements excluding `.` and `..` for the given path, non recursive.
     *
     * The custom consume_dir_item `digest` may also be used to filter the element, besides storing it.
     *
     * @param path path to directory
     * @param digest consume_dir_item function to receive each directory item, e.g. `void consume_dir_item(const dir_item& item)`
     * @return true if given path exists, is directory and is readable, otherwise false
     */
    bool get_dir_content(const std::string& path, const consume_dir_item& digest) noexcept;

    /**
     * Returns a list of directory elements excluding `.` and `..` for the given path, non recursive.
     *
     * The custom consume_dir_item `digest` may also be used to filter the element, besides storing it.
     *
     * @param dirfd file descriptor to given `path` left untouched as a copy is being used to retrieve the directory content.
     * @param path path to directory
     * @param digest consume_dir_item function to receive each directory item, e.g. `void consume_dir_item(const dir_item& item)`
     * @return true if given path exists, is directory and is readable, otherwise false
     */
    bool get_dir_content(const int dirfd, const std::string& path, const consume_dir_item& digest) noexcept;

    /**
     * Filesystem traverse event used to call path_visitor for path elements from visit().
     *
     * This `enum class` type fulfills `C++ named requirements: BitmaskType`.
     *
     * @see path_visitor
     * @see visit()
     */
    enum class traverse_event : uint16_t {
        /** No value, neither file, symlink nor dir_entry or dir_exit. Implying an error state in file_stat, e.g. !file_stats::has_access(). */
        none = 0,

        /**
         * Visiting a symbolic-link, either to a file or a non-existing entity. Not followed symbolic-links to a directory is expressed via dir_symlink.
         *
         * In case of a symbolic-link to an existing file, file is also set, i.e. file_symlink.
         */
        symlink = 1 << 0,

        /** Visiting a file, may be in conjunction with symlink, i.e. file_symlink. */
        file = 1 << 1,

        /** Visiting a symlink to a file, i.e. symlink | file  */
        file_symlink = symlink | file,

        /**
         * Visiting a symbolic-link to a directory which is not followed, i.e. traverse_options::follow_symlinks not set.
         */
        dir_symlink = 1 << 2,

        /**
         * Visiting a directory on entry, see traverse_options::dir_check_entry.
         *
         * This allows the path_visitor to deny traversal into the directory by returning false,
         * otherwise continuing traversal.
         */
        dir_check_entry = 1 << 7,

        /**
         * Visiting a directory on entry, see traverse_options::dir_entry.
         *
         * If a directory is visited non-recursive, i.e. traverse_options::recursive not set,
         * dir_entry and dir_exit are set, see dir_non_recursive.
         *
         * If a directory is a symbolic link which is not followed, i.e. traverse_options::follow_symlinks not set,
         * dir_symlink is used instead.
         */
        dir_entry = 1 << 8,

        /**
         * Visiting a directory on exit, see traverse_options::dir_exit.
         *
         * If a directory is visited non-recursive, i.e. traverse_options::recursive not set,
         * dir_entry and dir_exit are set, see dir_non_recursive.
         *
         * If a directory is a symbolic link which is not followed, i.e. traverse_options::follow_symlinks not set,
         * dir_symlink is used instead.
         */
        dir_exit = 1 << 9,

        /**
         * Visiting a directory non-recursive, i.e. traverse_options::recursive not set.
         *
         * Value is a bit-mask of dir_entry | dir_exit
         */
        dir_non_recursive = dir_entry | dir_exit
    };
    constexpr uint16_t number(const traverse_event rhs) noexcept {
        return static_cast<uint16_t>(rhs);
    }
    constexpr traverse_event operator~(const traverse_event rhs) noexcept {
        return static_cast<traverse_event>(~number(rhs));
    }
    constexpr traverse_event operator^(const traverse_event lhs, const traverse_event rhs) noexcept {
        return static_cast<traverse_event>(number(lhs) ^ number(rhs));
    }
    constexpr traverse_event operator|(const traverse_event lhs, const traverse_event rhs) noexcept {
        return static_cast<traverse_event>(number(lhs) | number(rhs));
    }
    constexpr traverse_event operator&(const traverse_event lhs, const traverse_event rhs) noexcept {
        return static_cast<traverse_event>(number(lhs) & number(rhs));
    }
    constexpr traverse_event& operator|=(traverse_event& lhs, const traverse_event rhs) noexcept {
        lhs = static_cast<traverse_event>(number(lhs) | number(rhs));
        return lhs;
    }
    constexpr traverse_event& operator&=(traverse_event& lhs, const traverse_event rhs) noexcept {
        lhs = static_cast<traverse_event>(number(lhs) & number(rhs));
        return lhs;
    }
    constexpr traverse_event& operator^=(traverse_event& lhs, const traverse_event rhs) noexcept {
        lhs = static_cast<traverse_event>(number(lhs) ^ number(rhs));
        return lhs;
    }
    constexpr bool operator==(const traverse_event lhs, const traverse_event rhs) noexcept {
        return number(lhs) == number(rhs);
    }
    constexpr bool operator!=(const traverse_event lhs, const traverse_event rhs) noexcept {
        return !(lhs == rhs);
    }
    constexpr bool is_set(const traverse_event mask, const traverse_event bit) noexcept {
        return bit == (mask & bit);
    }
    std::string to_string(const traverse_event mask) noexcept;

    /**
     * path_visitor jau::FunctionDef definition
     * - `bool visitor(traverse_event tevt, const file_stats& item_stats, size_t depth)`
     *
     * Depth being the recursive directory depth starting with 1 for the initial directory.
     *
     * Returning `false` stops traversal in general but traverse_options::dir_check_entry
     * will only skip traversing the denied directory.
     */
    typedef jau::function<bool(traverse_event, const file_stats&, size_t)> path_visitor;

    /**
     * Filesystem traverse options used to visit() path elements.
     *
     * This `enum class` type fulfills `C++ named requirements: BitmaskType`.
     *
     * @see visit()
     * @see remove()
     */
    enum class traverse_options : uint16_t {
        /** No option set */
        none = 0,

        /** Traverse through directories, i.e. perform visit, copy, remove etc actions recursively throughout the directory structure. */
        recursive = 1U << 0,

        /** Traverse through symbolic linked directories if traverse_options::recursive is set, i.e. directories with property fmode_t::link set. */
        follow_symlinks = 1U << 1,

        /** Traverse through elements in lexicographical order. This might be required when computing an order dependent outcome like a hash value. */
        lexicographical_order = 1U << 2,

        /** Call path_visitor at directory entry, allowing path_visitor to skip traversal of this directory if returning false. */
        dir_check_entry = 1U << 7,

        /** Call path_visitor at directory entry. Both, dir_entry and dir_exit can be set, only one or none. */
        dir_entry = 1U << 8,

        /** Call path_visitor at directory exit. Both, dir_entry and dir_exit can be set, only one or none. */
        dir_exit = 1U << 9,

        /** Enable verbosity mode, potentially used by a path_visitor implementation like remove(). */
        verbose = 1U << 15
    };
    constexpr uint16_t number(const traverse_options rhs) noexcept {
        return static_cast<uint16_t>(rhs);
    }
    constexpr traverse_options operator~(const traverse_options rhs) noexcept {
        return static_cast<traverse_options>(~number(rhs));
    }
    constexpr traverse_options operator^(const traverse_options lhs, const traverse_options rhs) noexcept {
        return static_cast<traverse_options>(number(lhs) ^ number(rhs));
    }
    constexpr traverse_options operator|(const traverse_options lhs, const traverse_options rhs) noexcept {
        return static_cast<traverse_options>(number(lhs) | number(rhs));
    }
    constexpr traverse_options operator&(const traverse_options lhs, const traverse_options rhs) noexcept {
        return static_cast<traverse_options>(number(lhs) & number(rhs));
    }
    constexpr traverse_options& operator|=(traverse_options& lhs, const traverse_options rhs) noexcept {
        lhs = static_cast<traverse_options>(number(lhs) | number(rhs));
        return lhs;
    }
    constexpr traverse_options& operator&=(traverse_options& lhs, const traverse_options rhs) noexcept {
        lhs = static_cast<traverse_options>(number(lhs) & number(rhs));
        return lhs;
    }
    constexpr traverse_options& operator^=(traverse_options& lhs, const traverse_options rhs) noexcept {
        lhs = static_cast<traverse_options>(number(lhs) ^ number(rhs));
        return lhs;
    }
    constexpr bool operator==(const traverse_options lhs, const traverse_options rhs) noexcept {
        return number(lhs) == number(rhs);
    }
    constexpr bool operator!=(const traverse_options lhs, const traverse_options rhs) noexcept {
        return !(lhs == rhs);
    }
    constexpr bool is_set(const traverse_options mask, const traverse_options bit) noexcept {
        return bit == (mask & bit);
    }
    std::string to_string(const traverse_options mask) noexcept;

    /**
     * Visit element(s) of a given path, see traverse_options for detailed settings.
     *
     * All elements of type fmode_t::file, fmode_t::dir and fmode_t::no_access or fmode_t::not_existing
     * will be visited by the given path_visitor `visitor`.
     *
     * Depth passed to path_visitor is the recursive directory depth and starts with 1 for the initial directory.
     *
     * path_visitor returning `false` stops traversal in general but traverse_options::dir_check_entry
     * will only skip traversing the denied directory.
     *
     * @param path the starting path
     * @param topts given traverse_options for this operation
     * @param visitor path_visitor function `bool visitor(const file_stats& item_stats, size_t depth)`.
     * @param dirfds optional empty `dirfd` stack pointer defaults to nullptr.
     *        If user provided, exposes the used `dirfd` stack, which last entry represents the currently visited directory.
     *        The `dirfd` stack starts and ends empty, i.e. all directory file descriptor are closed.
     *        In case of recursive directory traversion, the initial dir_entry visit starts with depth 1 and 2 fds, its parent and current directory.
     * @return true if successful including no path_visitor stopped traversal by returning `false` excluding traverse_options::dir_check_entry.
     */
    bool visit(const std::string& path, const traverse_options topts, const path_visitor& visitor, std::vector<int>* dirfds = nullptr) noexcept;

    /**
     * Visit element(s) of a given path, see traverse_options for detailed settings.
     *
     * All elements of type fmode_t::file, fmode_t::dir and fmode_t::no_access or fmode_t::not_existing
     * will be visited by the given path_visitor `visitor`.
     *
     * Depth passed to path_visitor is the recursive directory depth and starts with 1 for the initial directory.
     *
     * path_visitor returning `false` stops traversal in general but traverse_options::dir_check_entry
     * will only skip traversing the denied directory.
     *
     * @param item_stats pre-fetched file_stats for a given dir_item, used for efficiency
     * @param topts given traverse_options for this operation
     * @param visitor path_visitor function `bool visitor(const file_stats& item_stats, size_t depth)`.
     * @param dirfds optional empty `dirfd` stack pointer defaults to nullptr.
     *        If user provided, exposes the used `dirfd` stack, which last entry represents the currently visited directory.
     *        The `dirfd` stack starts and ends empty, i.e. all directory file descriptor are closed.
     *        In case of recursive directory traversion, the initial dir_entry visit starts with depth 1 and 2 fds, its parent and current directory.
     * @return true if successful including no path_visitor stopped traversal by returning `false` excluding traverse_options::dir_check_entry.
     */
    bool visit(const file_stats& item_stats, const traverse_options topts, const path_visitor& visitor, std::vector<int>* dirfds = nullptr) noexcept;

    /**
     * Remove the given path. If path represents a director, `recursive` must be set to true.
     *
     * The given traverse_options `options` are handled as follows:
     * - traverse_options::parent_dir_last will be added by implementation to operate correct
     * - traverse_options::recursive shall shall be set by caller to remove directories
     * - traverse_options::follow_symlinks shall be set by caller to remove symbolic linked directories recursively, which is kind of dangerous.
     *   If not set, only the symbolic link will be removed (default)
     *
     * Implementation is most data-race-free (DRF), utilizes following safeguards
     * - utilizing parent directory file descriptor and `openat()` and `unlinkat()` operations against concurrent mutation
     *
     * @param path path to remove
     * @param topts given traverse_options for this operation, defaults to traverse_options::none
     * @return true only if the file or the directory with content has been deleted, otherwise false
     */
    bool remove(const std::string& path, const traverse_options topts = traverse_options::none) noexcept;

    /**
     * Compare the bytes of both files, denoted by source1 and source2.
     *
     * @param source1 first source file to compare
     * @param source2 second source file to compare
     * @param verbose defaults to false
     * @return true if both elements are files and their bytes are equal, otherwise false.
     */
    bool compare(const file_stats& source1, const file_stats& source2, const bool verbose = false) noexcept;

    /**
     * Compare the bytes of both files, denoted by source1 and source2.
     *
     * @param source1 first source file to compare