diff --git a/include/fast_io_hosted/platforms/systemcall_details.h b/include/fast_io_hosted/platforms/systemcall_details.h index 9675aafe..a1c3e6bd 100644 --- a/include/fast_io_hosted/platforms/systemcall_details.h +++ b/include/fast_io_hosted/platforms/systemcall_details.h @@ -2,11 +2,12 @@ namespace fast_io::details { - -#ifdef __MSDOS__ +namespace posix +{ +#if defined(__MSDOS__) || defined(__DJGPP__) extern int dup(int) noexcept __asm__("_dup"); extern int dup2(int, int) noexcept __asm__("_dup2"); -extern int _close(int) noexcept __asm__("_close"); +extern int close(int) noexcept __asm__("_close"); #elif defined(__wasi__) inline int dup(int) noexcept { @@ -19,15 +20,20 @@ inline int dup2(int old_fd, int new_fd) noexcept return ::fast_io::noexcept_call(__wasi_fd_renumber, old_fd, new_fd); } #endif +} // namespace posix inline int sys_dup(int old_fd) { #if defined(__linux__) && defined(__NR_dup) - int fd{system_call<__NR_dup, int>(old_fd)}; - system_call_throw_error(fd); + int fd{::fast_io::system_call<__NR_dup, int>(old_fd)}; + ::fast_io::system_call_throw_error(fd); return fd; #else - auto fd{noexcept_call( + +#if (defined(__MSDOS__) || defined(__DJGPP__)) || defined(__wasi__) + auto fd{::fast_io::details::posix::dup(old_fd)}; +#else + auto fd{::fast_io::noexcept_call( #if defined(_WIN32) && !defined(__BIONIC__) ::_dup #else @@ -35,6 +41,8 @@ inline int sys_dup(int old_fd) #endif , old_fd)}; +#endif + if (fd == -1) { throw_posix_error(); @@ -47,18 +55,24 @@ template inline int sys_dup2(int old_fd, int new_fd) { #if defined(__linux__) && defined(__NR_dup2) - int fd{system_call<__NR_dup2, int>(old_fd, new_fd)}; - system_call_throw_error(fd); + int fd{::fast_io::system_call<__NR_dup2, int>(old_fd, new_fd)}; + ::fast_io::system_call_throw_error(fd); return fd; #else - auto fd{noexcept_call( + +#if (defined(__MSDOS__) || defined(__DJGPP__)) || defined(__wasi__) + auto fd{::fast_io::details::posix::dup2(old_fd, new_fd)}; +#else + auto fd{::fast_io::noexcept_call( #if defined(_WIN32) && !defined(__BIONIC__) - _dup2 + ::_dup2 #else - dup2 + ::dup2 #endif , old_fd, new_fd)}; +#endif + if (fd == -1) { if constexpr (always_terminate) @@ -83,20 +97,26 @@ struct return_code inline return_code sys_dup2_nothrow(int old_fd, int new_fd) noexcept { #if defined(__linux__) && defined(__NR_dup2) - int fd{system_call<__NR_dup2, int>(old_fd, new_fd)}; - if (linux_system_call_fails(fd)) + int fd{::fast_io::system_call<__NR_dup2, int>(old_fd, new_fd)}; + if (::fast_io::linux_system_call_fails(fd)) { return {-fd, true}; } #else - auto fd{noexcept_call( + +#if (defined(__MSDOS__) || defined(__DJGPP__)) || defined(__wasi__) + auto fd{::fast_io::details::posix::dup2(old_fd, new_fd)}; +#else + auto fd{::fast_io::noexcept_call( #if defined(_WIN32) && !defined(__BIONIC__) - _dup2 + ::_dup2 #else dup2 #endif , old_fd, new_fd)}; +#endif + if (fd == -1) { return {errno, true}; @@ -109,24 +129,26 @@ inline int sys_close(int fd) noexcept { return #if defined(__linux__) && defined(__NR_close) - system_call<__NR_close, int>(fd); -#elif (defined(_WIN32) && !defined(__BIONIC__)) || defined(__MSDOS__) - noexcept_call(_close, fd); + ::fast_io::system_call<__NR_close, int>(fd); +#elif (defined(_WIN32) && !defined(__BIONIC__)) + ::fast_io::noexcept_call(::_close, fd); +#elif defined(__MSDOS__) || defined(__DJGPP__) + ::fast_io::details::posix::close(fd); #else - noexcept_call(close, fd); + ::fast_io::noexcept_call(::close, fd); #endif } inline void sys_close_throw_error(int &fd) { - auto ret{sys_close(fd)}; + auto ret{::fast_io::details::sys_close(fd)}; fd = -1; // POSIX standard says we should never call close(2) again even close syscall fails #if defined(__linux__) && defined(__NR_close) - system_call_throw_error(ret); + ::fast_io::system_call_throw_error(ret); #else if (ret == -1) [[unlikely]] { - throw_posix_error(); + ::fast_io::throw_posix_error(); } #endif } @@ -148,14 +170,14 @@ template inline int sys_fcntl(int fd, int op, Args... args) { #if defined(__linux__) && defined(__NR_fcntl) - auto result{system_call<__NR_fcntl, int>(fd, op, args...)}; - system_call_throw_error(result); + auto result{::fast_io::system_call<__NR_fcntl, int>(fd, op, args...)}; + ::fast_io::system_call_throw_error(result); return result; #else auto result{posix::fcntl(fd, op, args...)}; if (result == -1) { - throw_posix_error(); + ::fast_io::throw_posix_error(); } return result; #endif diff --git a/include/fast_io_hosted/platforms/win32/msvc_linker_32.h b/include/fast_io_hosted/platforms/win32/msvc_linker_32.h index 76338f2f..992f053c 100644 --- a/include/fast_io_hosted/platforms/win32/msvc_linker_32.h +++ b/include/fast_io_hosted/platforms/win32/msvc_linker_32.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // This file have been generated by example/0039.win32mangling, therefore, do NOT edit this file directely! // clang-format off // WIN32 diff --git a/include/fast_io_hosted/platforms/win32/msvc_linker_32_i686.h b/include/fast_io_hosted/platforms/win32/msvc_linker_32_i686.h index f3dfc7e4..546ec290 100644 --- a/include/fast_io_hosted/platforms/win32/msvc_linker_32_i686.h +++ b/include/fast_io_hosted/platforms/win32/msvc_linker_32_i686.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // This file have been generated by example/0039.win32mangling, therefore, do NOT edit this file directely! // clang-format off // WIN32 diff --git a/include/fast_io_hosted/platforms/win32/msvc_linker_64.h b/include/fast_io_hosted/platforms/win32/msvc_linker_64.h index e9785805..964ecb55 100644 --- a/include/fast_io_hosted/platforms/win32/msvc_linker_64.h +++ b/include/fast_io_hosted/platforms/win32/msvc_linker_64.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // This file have been generated by example/0039.win32mangling, therefore, do NOT edit this file directely! // clang-format off // WIN32 diff --git a/include/fast_io_hosted/platforms/win32/msvc_linker_arm64ec.h b/include/fast_io_hosted/platforms/win32/msvc_linker_arm64ec.h index 01f0e4d2..7ae988cc 100644 --- a/include/fast_io_hosted/platforms/win32/msvc_linker_arm64ec.h +++ b/include/fast_io_hosted/platforms/win32/msvc_linker_arm64ec.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once // This file have been generated by example/0039.win32mangling, therefore, do NOT edit this file directely! // clang-format off // WIN32 diff --git a/include/fast_io_hosted/process/process/arg_env.h b/include/fast_io_hosted/process/process/arg_env.h index 858c233c..8d69663b 100644 --- a/include/fast_io_hosted/process/process/arg_env.h +++ b/include/fast_io_hosted/process/process/arg_env.h @@ -23,51 +23,94 @@ inline constexpr void append_win32_quoted_arg_common( ::fast_io::containers::basic_string &str, Iter first, Iter last) { - // Reserve rough upper bound: quotes + worst-case doubling - str.reserve(str.size() + 3 + static_cast<::std::size_t>(last - first) * 2u); - str.push_back_unchecked(::fast_io::char_literal_v); - - ::std::size_t backslash_count{}; - for (; first != last; ++first) + if (is_first) { - auto const c{*first}; - if (c == ::fast_io::char_literal_v) + bool needs_quote{}; + auto it{first}; + for (; it != last; ++it) { - if (is_first) [[unlikely]] + auto const c{*it}; + if (c == ::fast_io::char_literal_v) [[unlikely]] { - // Windows argv[0] does not allow double quotes even if escaped throw_win32_error(13); } - // Output 2*n+1 backslashes before a quote - for (::std::size_t i{}; i != ((backslash_count << 1u) + 1u); ++i) + if (c <= ::fast_io::char_literal_v) { - str.push_back_unchecked(::fast_io::char_literal_v); + needs_quote = true; } - str.push_back_unchecked(::fast_io::char_literal_v); - backslash_count = 0; } - else if (c == ::fast_io::char_literal_v) + + if (first == last) { - ++backslash_count; + str.push_back(::fast_io::char_literal_v); + str.push_back(::fast_io::char_literal_v); + str.push_back(::fast_io::char_literal_v); + return; } - else + + if (!needs_quote) { - // Flush pending backslashes (not before a quote): output as-is - for (::std::size_t i{}; i != backslash_count; ++i) + auto const sz{static_cast<::std::size_t>(last - first)}; + for (it = first; it != last; ++it) { - str.push_back_unchecked(::fast_io::char_literal_v); + str.push_back(*it); } - backslash_count = 0; - str.push_back_unchecked(c); + str.push_back(::fast_io::char_literal_v); + return; } + + auto const sz{static_cast<::std::size_t>(last - first)}; + str.push_back(::fast_io::char_literal_v); + for (it = first; it != last; ++it) + { + str.push_back(*it); + } + str.push_back(::fast_io::char_literal_v); + str.push_back(::fast_io::char_literal_v); } - // Before closing quote, double any trailing backslashes - for (::std::size_t i{}; i != (backslash_count << 1u); ++i) + else { - str.push_back_unchecked(::fast_io::char_literal_v); + // Reserve rough upper bound: quotes + worst-case doubling + str.reserve(str.size() + 3 + static_cast<::std::size_t>(last - first) * 2u); + str.push_back_unchecked(::fast_io::char_literal_v); + + ::std::size_t backslash_count{}; + for (; first != last; ++first) + { + auto const c{*first}; + if (c == ::fast_io::char_literal_v) + { + // Output 2*n+1 backslashes before a quote + for (::std::size_t i{}; i != ((backslash_count << 1u) + 1u); ++i) + { + str.push_back_unchecked(::fast_io::char_literal_v); + } + str.push_back_unchecked(::fast_io::char_literal_v); + backslash_count = 0; + } + else if (c == ::fast_io::char_literal_v) + { + ++backslash_count; + } + else + { + // Flush pending backslashes (not before a quote): output as-is + for (::std::size_t i{}; i != backslash_count; ++i) + { + str.push_back_unchecked(::fast_io::char_literal_v); + } + backslash_count = 0; + str.push_back_unchecked(c); + } + } + // Before closing quote, double any trailing backslashes + for (::std::size_t i{}; i != (backslash_count << 1u); ++i) + { + str.push_back_unchecked(::fast_io::char_literal_v); + } + str.push_back_unchecked(::fast_io::char_literal_v); + str.push_back_unchecked(::fast_io::char_literal_v); } - str.push_back_unchecked(::fast_io::char_literal_v); - str.push_back_unchecked(::fast_io::char_literal_v); } template <::std::integral replace_char_type, typename T> @@ -423,16 +466,16 @@ inline constexpr void construct_posix_process_argenvs_decay( namespace posix { #if defined(__APPLE__) || defined(__DARWIN_C_LEVEL) - // Darwin does not provide an `environ` function; here we use `_NSGetEnviron` to obtain it. - extern char*** _NSGetEnviron() noexcept __asm__("__NSGetEnviron"); +// Darwin does not provide an `environ` function; here we use `_NSGetEnviron` to obtain it. +extern char ***_NSGetEnviron() noexcept __asm__("__NSGetEnviron"); #elif defined(__MSDOS__) || defined(__DJGPP__) - // djgpp only provides `char** _environ`. For consistency, a symbolic link is used here. - extern char** environ __asm__("__environ"); +// djgpp only provides `char** _environ`. For consistency, a symbolic link is used here. +extern char **environ __asm__("__environ"); #elif !(defined(_WIN32) || defined(__CYGWIN__)) - // Reference to the global `environ` variable - extern "C" char** environ; +// Reference to the global `environ` variable +extern "C" char **environ; #endif -} // namespace details +} // namespace posix } // namespace details struct posix_process_args FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE diff --git a/include/fast_io_hosted/process/process/posix.h b/include/fast_io_hosted/process/process/posix.h index e9fd621a..e8f633f6 100644 --- a/include/fast_io_hosted/process/process/posix.h +++ b/include/fast_io_hosted/process/process/posix.h @@ -184,7 +184,7 @@ inline void portable_fd_path([[maybe_unused]] int fd, char *buf, ::std::size_t b ::fast_io::obuffer_view linkpath_ov{linkpath, linkpath + all_sz}; ::fast_io::operations::print_freestanding(linkpath_ov, path_str, fd, ::fast_io::mnp::chvw(::fast_io::char_literal_v)); - using my_ssize_t [[maybe_unused]] = ::std::make_signed_t<::std::size_t>; + using my_ssize_t [[maybe_unused]] = ::std::make_signed_t<::std::size_t>; #if defined(__linux__) && defined(__NR_readlink) auto resolved{::fast_io::system_call<__NR_readlink, my_ssize_t>(linkpath, buf, bufsz - 1u)}; @@ -716,7 +716,7 @@ struct fd_remapper }; // only used in vfork_execveat_common_impl() -inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char const *const *args, char const *const *envp, sig_atomic_t volatile &t_errno, process_mode mode) noexcept +inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char const *const *args, char const *const *envp, unsigned volatile &t_errno, process_mode mode) noexcept { // vfork can only be called through libc wrapper pid = ::fast_io::posix::libc_vfork(); @@ -810,7 +810,7 @@ inline void vfork_and_execveat(pid_t &pid, int dirfd, char const *cstr, char con inline pid_t vfork_execveat_common_impl(int dirfd, char const *cstr, char const *const *args, char const *const *envp, posix_process_io const &pio, process_mode mode) { pid_t pid{}; - sig_atomic_t volatile t_errno{}; // receive error from vfork subproc + unsigned volatile t_errno{}; // receive error from vfork subproc { fd_remapper fm; fm.map(0, pio.in); diff --git a/include/fast_io_hosted/threads/thread/dos.h b/include/fast_io_hosted/threads/thread/dos.h new file mode 100644 index 00000000..e9c24962 --- /dev/null +++ b/include/fast_io_hosted/threads/thread/dos.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace fast_io +{ +namespace details::posix +{ +extern unsigned my_usleep(unsigned usec) noexcept __asm__("_usleep"); +} // namespace details::posix + +namespace dos::this_thread +{ + +using dos_thread_id = unsigned; + +inline constexpr dos_thread_id get_id() noexcept +{ + return 0u; +} + +template +inline +#if __cpp_constexpr >= 202207L + // https://en.cppreference.com/w/cpp/compiler_support/23.html#cpp_constexpr_202207L + // for reduce some warning purpose + constexpr +#endif + void sleep_for(::std::chrono::duration const &sleep_duration) noexcept +{ + auto const us64{::std::chrono::duration_cast<::std::chrono::microseconds>(sleep_duration).count()}; + if (us64 <= 0) + { + return; + } + auto remaining{static_cast<::std::uint_least64_t>(us64)}; + constexpr ::std::uint_least64_t max_chunk{static_cast<::std::uint_least64_t>(static_cast(-1))}; + while (remaining != 0) + { + auto const chunk{remaining > max_chunk ? max_chunk : remaining}; + ::fast_io::details::posix::my_usleep(static_cast(chunk)); + remaining -= chunk; + } +} + +template +inline +#if __cpp_constexpr >= 202207L + // https://en.cppreference.com/w/cpp/compiler_support/23.html#cpp_constexpr_202207L + // for reduce some warning purpose + constexpr +#endif + void sleep_until(::std::chrono::time_point const &expect_time) noexcept +{ + auto const now{Clock::now()}; + if (now < expect_time) + { + ::fast_io::dos::this_thread::sleep_for(expect_time - now); + } +} + +template <::std::int_least64_t off_to_epoch> +inline +#if __cpp_constexpr >= 202207L + // https://en.cppreference.com/w/cpp/compiler_support/23.html#cpp_constexpr_202207L + // for reduce some warning purpose + constexpr +#endif + void sleep_for(::fast_io::basic_timestamp const &sleep_duration) noexcept +{ + if (sleep_duration.seconds < 0) + { + return; + } + + constexpr ::std::uint_least64_t mul_factor{::fast_io::uint_least64_subseconds_per_second / 1000000u}; + + auto const us64{ + static_cast<::std::uint_least64_t>(static_cast<::std::uint_least64_t>(sleep_duration.seconds) * 1000000u + + sleep_duration.subseconds / mul_factor)}; + if (us64 == 0) + { + return; + } + + auto remaining{us64}; + constexpr ::std::uint_least64_t max_chunk{static_cast<::std::uint_least64_t>(static_cast(-1))}; + while (remaining != 0) + { + auto const chunk{remaining > max_chunk ? max_chunk : remaining}; + ::fast_io::details::posix::my_usleep(static_cast(chunk)); + remaining -= chunk; + } +} + +template <::std::int_least64_t off_to_epoch> +inline +#if __cpp_constexpr >= 202207L + // https://en.cppreference.com/w/cpp/compiler_support/23.html#cpp_constexpr_202207L + // for reduce some warning purpose + constexpr +#endif + void sleep_until(::fast_io::basic_timestamp const &expect_time) noexcept +{ + if (expect_time.seconds < 0) + { + return; + } + + auto const unix_expect{static_cast<::fast_io::unix_timestamp>(expect_time)}; + auto const now{::fast_io::posix_clock_gettime(::fast_io::posix_clock_id::realtime)}; + if (!(now < unix_expect)) + { + return; + } + + auto const delta{unix_expect - now}; + ::fast_io::dos::this_thread::sleep_for(delta); +} +} // namespace dos::this_thread + +#if defined(__MSDOS__) || defined(__DJGPP__) + +namespace this_thread +{ +using ::fast_io::dos::this_thread::get_id; +using ::fast_io::dos::this_thread::sleep_for; +using ::fast_io::dos::this_thread::sleep_until; +} // namespace this_thread +#endif + +} // namespace fast_io diff --git a/include/fast_io_hosted/threads/thread/impl.h b/include/fast_io_hosted/threads/thread/impl.h index 1f6677ba..67b20ab8 100644 --- a/include/fast_io_hosted/threads/thread/impl.h +++ b/include/fast_io_hosted/threads/thread/impl.h @@ -5,6 +5,8 @@ #ifndef _WIN32_WINDOWS #include "nt.h" #endif +#elif defined(__MSDOS__) || defined(__DJGPP__) +#include "dos.h" #elif !defined(__SINGLE_THREAD__) && !defined(__NEWLIB__) && \ !defined(__MSDOS__) && !defined(__wasi__) && __has_include() #include "pthread.h" diff --git a/include/fast_io_hosted/threads/thread/stack_pointer.h b/include/fast_io_hosted/threads/thread/stack_pointer.h index cd789a8e..86f1c8b6 100644 --- a/include/fast_io_hosted/threads/thread/stack_pointer.h +++ b/include/fast_io_hosted/threads/thread/stack_pointer.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #if defined (_MSC_VER) && !defined(__clang__) #error "get_stack_pointer is not supported on MSVC"