diff options
author | crupest <crupest@outlook.com> | 2024-06-24 00:06:25 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-08-18 16:50:20 +0800 |
commit | 1b30150ab79ff1338f209a8ddb54b3dc60cfb599 (patch) | |
tree | 97e183587b293ecf768476da0edf3fdcf86e4543 | |
parent | b756bf519cda0684ec46d0d9404cbc59741ec0cb (diff) | |
download | cru-1b30150ab79ff1338f209a8ddb54b3dc60cfb599.tar.gz cru-1b30150ab79ff1338f209a8ddb54b3dc60cfb599.tar.bz2 cru-1b30150ab79ff1338f209a8ddb54b3dc60cfb599.zip |
fix(SubProcess): fix pipe fs close, add tests.
NEED TEST: BufferStream, AutoReadStream, SubProcess.
-rw-r--r-- | include/cru/common/Bitmask.h | 5 | ||||
-rw-r--r-- | include/cru/common/Format.h | 59 | ||||
-rw-r--r-- | include/cru/common/String.h | 32 | ||||
-rw-r--r-- | include/cru/common/StringToNumberConverter.h | 88 | ||||
-rw-r--r-- | include/cru/common/SubProcess.h | 2 | ||||
-rw-r--r-- | include/cru/common/platform/unix/UnixPipe.h | 3 | ||||
m--------- | lib/Catch2 | 0 | ||||
m--------- | lib/double-conversion | 0 | ||||
-rw-r--r-- | src/common/Format.cpp | 19 | ||||
-rw-r--r-- | src/common/String.cpp | 36 | ||||
-rw-r--r-- | src/common/StringToNumberConverter.cpp | 61 | ||||
-rw-r--r-- | src/common/io/MemoryStream.cpp | 2 | ||||
-rw-r--r-- | src/common/platform/unix/PosixSpawnSubProcess.cpp | 44 | ||||
-rw-r--r-- | src/common/platform/unix/UnixPipe.cpp | 12 | ||||
-rw-r--r-- | test/common/CMakeLists.txt | 13 | ||||
-rw-r--r-- | test/common/StringToNumberConverterTest.cpp | 70 | ||||
-rw-r--r-- | test/common/SubProcessHelper/CruEcho.cpp | 9 | ||||
-rw-r--r-- | test/common/SubProcessHelper/CruTee.cpp | 10 | ||||
-rw-r--r-- | test/common/SubProcessTest.cpp | 21 |
19 files changed, 286 insertions, 200 deletions
diff --git a/include/cru/common/Bitmask.h b/include/cru/common/Bitmask.h index 23a3a069..9b6b8957 100644 --- a/include/cru/common/Bitmask.h +++ b/include/cru/common/Bitmask.h @@ -1,4 +1,5 @@ #pragma once + #include <functional> namespace cru { @@ -13,6 +14,8 @@ struct Bitmask final { return Bitmask(static_cast<TUnderlying>(1u << offset)); } + constexpr bool Has(Bitmask rhs) const { return (value & rhs.value) != 0; } + Bitmask operator|(Bitmask rhs) const { return Bitmask(value | rhs.value); } Bitmask operator&(Bitmask rhs) const { return Bitmask(value & rhs.value); } Bitmask operator^(Bitmask rhs) const { return Bitmask(value ^ rhs.value); } @@ -34,7 +37,7 @@ struct Bitmask final { bool operator!=(Bitmask rhs) const { return this->value != rhs.value; } explicit operator TUnderlying() const { return value; } - explicit operator bool() const { return value != 0; } + operator bool() const { return value != 0; } TUnderlying value; }; diff --git a/include/cru/common/Format.h b/include/cru/common/Format.h index e6935343..d5c5ed99 100644 --- a/include/cru/common/Format.h +++ b/include/cru/common/Format.h @@ -3,8 +3,10 @@ #include "Exception.h" #include "String.h" -#include <double-conversion/double-conversion.h> -#include <charconv> +#include <cassert> +#include <cstdio> +#include <type_traits> +#include <vector> namespace cru { inline String ToString(bool value) { @@ -12,31 +14,40 @@ inline String ToString(bool value) { } template <typename T> -std::enable_if_t<std::is_integral_v<T>, String> ToString(T value) { - std::array<char, 50> buffer; - auto result = - std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); - - if (result.ec == std::errc{}) { - } else { - throw std::invalid_argument("Failed to convert value to chars."); - } +inline constexpr std::nullptr_t kPrintfFormatSpecifierOfType = nullptr; + +#define CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(type, specifier) \ + template <> \ + inline constexpr const char* kPrintfFormatSpecifierOfType<type> = specifier; + +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(signed char, "%c") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(unsigned char, "%c") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(signed short, "%hd") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(unsigned short, "%hu") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(signed int, "%d") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(unsigned int, "%u") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(signed long, "%ld") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(unsigned long, "%lu") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(signed long long, "%lld") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(unsigned long long, "%llu") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(float, "%f") +CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE(double, "%f") + +#undef CRU_DEFINE_PRINTF_FORMAT_SPECIFIER_OF_TYPE - auto size = result.ptr - buffer.data(); - auto b = new char16_t[size + 1]; - b[size] = 0; - std::copy(buffer.data(), result.ptr, b); - return String::FromBuffer(b, size, size); +template <typename T> +std::enable_if_t< + !std::is_null_pointer_v<decltype(kPrintfFormatSpecifierOfType<T>)>, String> +ToString(T value) { + auto size = std::snprintf(nullptr, 0, kPrintfFormatSpecifierOfType<T>, value); + assert(size > 0); + std::vector<char> buffer(size + 1); + size = std::snprintf(buffer.data(), size + 1, kPrintfFormatSpecifierOfType<T>, + value); + assert(size > 0); + return String::FromUtf8(buffer.data(), size); } -extern double_conversion::DoubleToStringConverter - kDefaultDoubleToStringConverter; - -String CRU_BASE_API ToString(float value, StringView option); -String CRU_BASE_API ToString(double value, StringView option); -inline String ToString(float value) { return ToString(value, u""); } -inline String ToString(double value) { return ToString(value, u""); } - template <typename T> String ToString(const T& value, StringView option) { CRU_UNUSED(option) diff --git a/include/cru/common/String.h b/include/cru/common/String.h index 5d9fc549..21a3db51 100644 --- a/include/cru/common/String.h +++ b/include/cru/common/String.h @@ -6,7 +6,6 @@ #include "StringToNumberConverter.h" #include "StringUtil.h" -#include <double-conversion/double-conversion.h> #include <filesystem> #include <initializer_list> #include <iterator> @@ -221,16 +220,14 @@ class CRU_BASE_API String { Index* processed_characters_count, unsigned flags, int base) const; int ParseToInt(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags, - int base = 0) const; + StringToNumberFlag flags = {}, int base = 0) const; long long ParseToLongLong(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags, - int base = 0) const; + StringToNumberFlag flags = {}, int base = 0) const; float ParseToFloat(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags) const; + StringToNumberFlag flags = {}) const; double ParseToDouble(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags) const; + StringToNumberFlag flags = {}) const; std::vector<float> ParseToFloatList(value_type separator = u' ') const; std::vector<double> ParseToDoubleList(value_type separator = u' ') const; @@ -261,6 +258,8 @@ class CRU_BASE_API String { Index capacity_ = 0; // always 1 smaller than real buffer size }; +std::ostream& CRU_BASE_API operator<<(std::ostream& os, const String& value); + class CRU_BASE_API StringView { public: using value_type = char16_t; @@ -357,23 +356,24 @@ class CRU_BASE_API StringView { template <typename TInteger> std::enable_if_t<std::is_signed_v<TInteger>, TInteger> ParseToInteger( - Index* processed_characters_count, unsigned flags, int base) const { - auto result = StringToIntegerConverterImpl(flags, base) - .Parse(data(), size(), processed_characters_count); + Index* processed_characters_count, StringToNumberFlag flags, + int base) const { + auto utf8_string = ToUtf8(); + auto result = StringToIntegerConverter(flags, base) + .Parse(utf8_string.data(), utf8_string.size(), + processed_characters_count); return result.negate ? -result.value : result.value; } int ParseToInt(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags, - int base = 0) const; + StringToNumberFlag flags = {}, int base = 0) const; long long ParseToLongLong(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags, - int base = 0) const; + StringToNumberFlag flags = {}, int base = 0) const; float ParseToFloat(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags) const; + StringToNumberFlag flags = {}) const; double ParseToDouble(Index* processed_characters_count = nullptr, - unsigned flags = StringToNumberFlags::kNoFlags) const; + StringToNumberFlag flags = {}) const; std::vector<float> ParseToFloatList(value_type separator = u' ') const; std::vector<double> ParseToDoubleList(value_type separator = u' ') const; diff --git a/include/cru/common/StringToNumberConverter.h b/include/cru/common/StringToNumberConverter.h index f1baafee..758b26c8 100644 --- a/include/cru/common/StringToNumberConverter.h +++ b/include/cru/common/StringToNumberConverter.h @@ -1,41 +1,63 @@ #pragma once #include "Base.h" +#include "Bitmask.h" +#include <cstddef> #include <ostream> namespace cru { -struct CRU_BASE_API StringToNumberFlags { - constexpr static unsigned kNoFlags = 0; - constexpr static unsigned kAllowLeadingSpaces = 1 << 0; - constexpr static unsigned kAllowTrailingSpaces = 1 << 1; - constexpr static unsigned kAllowTrailingJunk = 1 << 2; - constexpr static unsigned kAllowLeadingZeroForInteger = 1 << 2; - constexpr static unsigned kThrowOnError = 1 << 3; +namespace details { +struct StringToNumberFlagTag {}; +} // namespace details + +using StringToNumberFlag = Bitmask<details::StringToNumberFlagTag>; + +struct StringToNumberFlags { + constexpr static StringToNumberFlag kAllowLeadingSpaces = + StringToNumberFlag::FromOffset(0); + constexpr static StringToNumberFlag kAllowTrailingSpaces = + StringToNumberFlag::FromOffset(1); + constexpr static StringToNumberFlag kAllowTrailingJunk = + StringToNumberFlag::FromOffset(2); + constexpr static StringToNumberFlag kAllowLeadingZeroForInteger = + StringToNumberFlag::FromOffset(3); + constexpr static StringToNumberFlag kThrowOnError = + StringToNumberFlag::FromOffset(4); }; -struct CRU_BASE_API StringToIntegerConverterImplResult { - StringToIntegerConverterImplResult() = default; - StringToIntegerConverterImplResult(bool negate, unsigned long long value) +template <typename TResult> +struct IStringToNumberConverter : virtual Interface { + virtual TResult Parse(const char* str, Index size, + Index* processed_characters_count) const = 0; + + template <std::size_t Size> + TResult Parse(const char (&str)[Size], + Index* processed_characters_count) const { + return Parse(str, Size - 1, processed_characters_count); + } +}; + +struct CRU_BASE_API StringToIntegerResult { + StringToIntegerResult() = default; + StringToIntegerResult(bool negate, unsigned long long value) : negate(negate), value(value) {} bool negate; unsigned long long value; }; -inline bool CRU_BASE_API -operator==(const StringToIntegerConverterImplResult& left, - const StringToIntegerConverterImplResult& right) { +inline bool CRU_BASE_API operator==(const StringToIntegerResult& left, + const StringToIntegerResult& right) { return left.negate == right.negate && left.value == right.value; } -inline bool CRU_BASE_API -operator!=(const StringToIntegerConverterImplResult& left, - const StringToIntegerConverterImplResult& right) { +inline bool CRU_BASE_API operator!=(const StringToIntegerResult& left, + const StringToIntegerResult& right) { return !(left == right); } -inline std::ostream& operator<<( - std::ostream& stream, const StringToIntegerConverterImplResult& result) { +inline std::ostream& CRU_BASE_API +operator<<(std::ostream& stream, const StringToIntegerResult& result) { return stream << "StringToIntegerConverterImplResult(" << (result.negate ? "-" : "") << result.value << ")"; } @@ -43,9 +65,10 @@ inline std::ostream& operator<<( /** * \brief A converter that convert number into long long. */ -struct CRU_BASE_API StringToIntegerConverterImpl { +struct CRU_BASE_API StringToIntegerConverter + : IStringToNumberConverter<StringToIntegerResult> { public: - explicit StringToIntegerConverterImpl(unsigned flags, int base = 0) + explicit StringToIntegerConverter(StringToNumberFlag flags, int base = 0) : flags(flags), base(base) {} bool CheckParams() const; @@ -57,19 +80,11 @@ struct CRU_BASE_API StringToIntegerConverterImpl { * \param processed_characters_count The number of characters that were * processed. Or nullptr to not retrieve. */ - StringToIntegerConverterImplResult Parse( - const char* str, Index size, Index* processed_characters_count) const; - - StringToIntegerConverterImplResult Parse( - const char16_t* str, Index size, Index* processed_characters_count) const; + StringToIntegerResult Parse(const char* str, Index size, + Index* processed_characters_count) const override; + using IStringToNumberConverter<StringToIntegerResult>::Parse; - template <std::size_t Size> - StringToIntegerConverterImplResult Parse( - const char (&str)[Size], Index* processed_characters_count) const { - return Parse(str, Size - 1, processed_characters_count); - } - - unsigned flags; + StringToNumberFlag flags; /** * \brief The base of the number used for parse or 0 for auto detect. * \remarks Base can only be of range [2, 36] or 0. If base is 0, decimal is @@ -80,4 +95,13 @@ struct CRU_BASE_API StringToIntegerConverterImpl { */ int base; }; + +struct CRU_BASE_API StringToFloatConverter { + StringToFloatConverter(StringToNumberFlag flags) : flags(flags) {} + + double Parse(const char* str, Index size, + Index* processed_characters_count) const; + + StringToNumberFlag flags; +}; } // namespace cru diff --git a/include/cru/common/SubProcess.h b/include/cru/common/SubProcess.h index 98c272a3..fbe8ad2b 100644 --- a/include/cru/common/SubProcess.h +++ b/include/cru/common/SubProcess.h @@ -231,7 +231,7 @@ class CRU_BASE_API SubProcess : public Object { SubProcess(SubProcess&& other) = default; SubProcess& operator=(SubProcess&& other) = default; - ~SubProcess(); + ~SubProcess() override; public: void Wait(std::optional<std::chrono::milliseconds> wait_time = std::nullopt); diff --git a/include/cru/common/platform/unix/UnixPipe.h b/include/cru/common/platform/unix/UnixPipe.h index afe6024a..cf35fb11 100644 --- a/include/cru/common/platform/unix/UnixPipe.h +++ b/include/cru/common/platform/unix/UnixPipe.h @@ -43,7 +43,7 @@ class UnixPipe : public Object { Receive, }; - explicit UnixPipe(Usage usage, UnixPipeFlag flags = {}); + explicit UnixPipe(Usage usage, bool auto_close, UnixPipeFlag flags = {}); CRU_DELETE_COPY(UnixPipe) CRU_DELETE_MOVE(UnixPipe) @@ -62,6 +62,7 @@ class UnixPipe : public Object { private: Usage usage_; + bool auto_close_; UnixPipeFlag flags_; int read_fd_; int write_fd_; diff --git a/lib/Catch2 b/lib/Catch2 -Subproject bfe3ff8f19d3436381b4e3a18c13734d21a8b08 +Subproject 85b7f3d6abbf11cfde34e274180a5539d2ac8bf diff --git a/lib/double-conversion b/lib/double-conversion -Subproject 15b7e306433dd899585f92758f7776a37a9c25f +Subproject 1c5f98155d5bce2db7adb6068770165177bd837 diff --git a/src/common/Format.cpp b/src/common/Format.cpp index 0ae744fc..d58c90b7 100644 --- a/src/common/Format.cpp +++ b/src/common/Format.cpp @@ -1,25 +1,6 @@ #include "cru/common/Format.h" -#include <double-conversion/utils.h> namespace cru { - -double_conversion::DoubleToStringConverter kDefaultDoubleToStringConverter( - 0, "infinity", "nan", 'e', -6, 21, 6, 1); - -String ToString(float value, StringView option) { - char buffer[50]; - double_conversion::StringBuilder string_builder(buffer, sizeof(buffer)); - kDefaultDoubleToStringConverter.ToShortestSingle(value, &string_builder); - return String::FromUtf8(std::string_view(buffer, string_builder.position())); -} - -String ToString(double value, StringView option) { - char buffer[50]; - double_conversion::StringBuilder string_builder(buffer, sizeof(buffer)); - kDefaultDoubleToStringConverter.ToShortestSingle(value, &string_builder); - return String::FromUtf8(std::string_view(buffer, string_builder.position())); -} - namespace details { FormatToken ParsePlaceHolder(String place_holder_string) { if (place_holder_string.empty()) { diff --git a/src/common/String.cpp b/src/common/String.cpp index ddfa03a9..27712f01 100644 --- a/src/common/String.cpp +++ b/src/common/String.cpp @@ -33,7 +33,8 @@ String String::FromUtf8(const char* str, Index size) { Utf8CodePointIterator iter(str, size); for (auto cp : iter) { Utf16EncodeCodePointAppend( - cp, std::bind(&String::push_back, std::ref(result), std::placeholders::_1)); + cp, + std::bind(&String::push_back, std::ref(result), std::placeholders::_1)); } return result; } @@ -330,23 +331,23 @@ Range String::RangeFromCodePointToCodeUnit(Range code_point_range) const { return View().RangeFromCodePointToCodeUnit(code_point_range); } -int String::ParseToInt(Index* processed_characters_count, unsigned flags, - int base) const { +int String::ParseToInt(Index* processed_characters_count, + StringToNumberFlag flags, int base) const { return View().ParseToInt(processed_characters_count, flags, base); } long long String::ParseToLongLong(Index* processed_characters_count, - unsigned flags, int base) const { + StringToNumberFlag flags, int base) const { return View().ParseToLongLong(processed_characters_count, flags, base); } float String::ParseToFloat(Index* processed_characters_count, - unsigned flags) const { + StringToNumberFlag flags) const { return View().ParseToFloat(processed_characters_count, flags); } double String::ParseToDouble(Index* processed_characters_count, - unsigned flags) const { + StringToNumberFlag flags) const { return View().ParseToDouble(processed_characters_count, flags); } @@ -358,6 +359,11 @@ std::vector<double> String::ParseToDoubleList(value_type separator) const { return View().ParseToDoubleList(separator); } +std::ostream& operator<<(std::ostream& os, const String& value) { + os << value.ToUtf8(); + return os; +} + namespace { inline int Compare(char16_t left, char16_t right) { if (left < right) return -1; @@ -540,7 +546,8 @@ std::string StringView::ToUtf8() const { std::string result; for (auto cp : CodePointIterator()) { Utf8EncodeCodePointAppend( - cp, std::bind(&std::string::push_back, std::ref(result), std::placeholders::_1)); + cp, std::bind(&std::string::push_back, std::ref(result), + std::placeholders::_1)); } return result; } @@ -563,17 +570,18 @@ Buffer StringView::ToUtf8Buffer(bool end_zero) const { return buffer; } -int StringView::ParseToInt(Index* processed_characters_count, unsigned flags, - int base) const { +int StringView::ParseToInt(Index* processed_characters_count, + StringToNumberFlag flags, int base) const { return ParseToInteger<int>(processed_characters_count, flags, base); } long long StringView::ParseToLongLong(Index* processed_characters_count, - unsigned flags, int base) const { + StringToNumberFlag flags, + int base) const { return ParseToInteger<long long>(processed_characters_count, flags, base); } -static int MapStringToDoubleFlags(int flags) { +static int MapStringToDoubleFlags(StringToNumberFlag flags) { int f = double_conversion::StringToDoubleConverter::ALLOW_CASE_INSENSIBILITY; if (flags & StringToNumberFlags::kAllowLeadingSpaces) { f |= double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES; @@ -588,12 +596,12 @@ static int MapStringToDoubleFlags(int flags) { } static double_conversion::StringToDoubleConverter CreateStringToDoubleConverter( - int flags) { + StringToNumberFlag flags) { return {MapStringToDoubleFlags(flags), 0.0, NAN, "inf", "nan"}; } float StringView::ParseToFloat(Index* processed_characters_count, - unsigned flags) const { + StringToNumberFlag flags) const { int pcc; auto result = CreateStringToDoubleConverter(flags).StringToFloat( reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc); @@ -609,7 +617,7 @@ float StringView::ParseToFloat(Index* processed_characters_count, } double StringView::ParseToDouble(Index* processed_characters_count, - unsigned flags) const { + StringToNumberFlag flags) const { int pcc; auto result = CreateStringToDoubleConverter(flags).StringToDouble( reinterpret_cast<const uc16*>(ptr_), static_cast<int>(size_), &pcc); diff --git a/src/common/StringToNumberConverter.cpp b/src/common/StringToNumberConverter.cpp index e20e436c..7a926d3d 100644 --- a/src/common/StringToNumberConverter.cpp +++ b/src/common/StringToNumberConverter.cpp @@ -2,7 +2,7 @@ #include "cru/common/Exception.h" namespace cru { -bool StringToIntegerConverterImpl::CheckParams() const { +bool StringToIntegerConverter::CheckParams() const { return base == 0 || base >= 2 & base <= 36; } @@ -10,24 +10,20 @@ static bool IsSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } -namespace { -template <typename T> -StringToIntegerConverterImplResult GenericParseInteger( - const StringToIntegerConverterImpl* converter, const T* const str, - const Index size, Index* processed_characters_count) { +StringToIntegerResult StringToIntegerConverter::Parse( + const char* const str, const Index size, + Index* processed_characters_count) const { if (str == nullptr) throw std::invalid_argument("Invalid str."); if (size < 0) throw std::invalid_argument("Invalid size."); - if (!converter->CheckParams()) - throw std::invalid_argument("Invalid parsing flags."); + if (!CheckParams()) throw std::invalid_argument("Invalid parsing flags."); - const bool throw_on_error = - (converter->flags & StringToNumberFlags::kThrowOnError) != 0; + const bool throw_on_error = flags.Has(StringToNumberFlags::kThrowOnError); auto const end = str + size; auto current = str; - if (converter->flags & StringToNumberFlags::kAllowLeadingSpaces) { + if (flags & StringToNumberFlags::kAllowLeadingSpaces) { while (current != end && IsSpace(*current)) { current++; } @@ -64,9 +60,9 @@ StringToIntegerConverterImplResult GenericParseInteger( } } - int base = converter->base; + int real_base = base; - if (base == 0) { + if (real_base == 0) { if (*current == '0') { ++current; if (current == end) { @@ -76,15 +72,15 @@ StringToIntegerConverterImplResult GenericParseInteger( return {negate, 0}; } else if (*current == 'x' || *current == 'X') { ++current; - base = 16; + real_base = 16; } else if (*current == 'b' || *current == 'B') { ++current; - base = 2; + real_base = 2; } else { - base = 8; + real_base = 8; } } else { - base = 10; + real_base = 10; } } @@ -100,7 +96,7 @@ StringToIntegerConverterImplResult GenericParseInteger( } const bool allow_leading_zero = - converter->flags & StringToNumberFlags::kAllowLeadingZeroForInteger; + flags.Has(StringToNumberFlags::kAllowLeadingZeroForInteger); while (current != end && *current == '0') { current++; @@ -114,21 +110,22 @@ StringToIntegerConverterImplResult GenericParseInteger( } const bool allow_trailing_junk = - converter->flags & StringToNumberFlags::kAllowTrailingJunk; + flags.Has(StringToNumberFlags::kAllowTrailingJunk); const bool allow_trailing_spaces = - converter->flags & StringToNumberFlags::kAllowTrailingSpaces; + flags.Has(StringToNumberFlags::kAllowTrailingSpaces); + unsigned long long result = 0; while (current != end) { const char c = *current; - if (c >= '0' && c <= (base > 10 ? '9' : base + '0' - 1)) { - result = result * base + c - '0'; + if (c >= '0' && c <= (real_base > 10 ? '9' : real_base + '0' - 1)) { + result = result * real_base + c - '0'; current++; - } else if (base > 10 && c >= 'a' && c <= (base + 'a' - 10 - 1)) { - result = result * base + c - 'a' + 10; + } else if (real_base > 10 && c >= 'a' && c <= (real_base + 'a' - 10 - 1)) { + result = result * real_base + c - 'a' + 10; current++; - } else if (base > 10 && c >= 'A' && c <= (base + 'A' - 10 - 1)) { - result = result * base + c - 'A' + 10; + } else if (real_base > 10 && c >= 'A' && c <= (real_base + 'A' - 10 - 1)) { + result = result * real_base + c - 'A' + 10; current++; } else if (allow_trailing_junk) { break; @@ -169,17 +166,5 @@ StringToIntegerConverterImplResult GenericParseInteger( return {negate, result}; } -} // namespace -StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( - const char* const str, const Index size, - Index* processed_characters_count) const { - return GenericParseInteger(this, str, size, processed_characters_count); -} - -StringToIntegerConverterImplResult StringToIntegerConverterImpl::Parse( - const char16_t* const str, const Index size, - Index* processed_characters_count) const { - return GenericParseInteger(this, str, size, processed_characters_count); -} } // namespace cru diff --git a/src/common/io/MemoryStream.cpp b/src/common/io/MemoryStream.cpp index 7507875b..34116193 100644 --- a/src/common/io/MemoryStream.cpp +++ b/src/common/io/MemoryStream.cpp @@ -1,5 +1,7 @@ #include "cru/common/io/MemoryStream.h" +#include <cstring> + namespace cru::io { MemoryStream::~MemoryStream() { if (release_func_) { diff --git a/src/common/platform/unix/PosixSpawnSubProcess.cpp b/src/common/platform/unix/PosixSpawnSubProcess.cpp index 8b521a5e..b5d68845 100644 --- a/src/common/platform/unix/PosixSpawnSubProcess.cpp +++ b/src/common/platform/unix/PosixSpawnSubProcess.cpp @@ -17,15 +17,15 @@ namespace cru::platform::unix { PosixSpawnSubProcessImpl::PosixSpawnSubProcessImpl() : pid_(0), exit_code_(0), - stdin_pipe_(UnixPipe::Usage::Send), - stdout_pipe_(UnixPipe::Usage::Receive), - stderr_pipe_(UnixPipe::Usage::Receive) { + stdin_pipe_(UnixPipe::Usage::Send, false), + stdout_pipe_(UnixPipe::Usage::Receive, false), + stderr_pipe_(UnixPipe::Usage::Receive, false) { stdin_stream_ = std::make_unique<UnixFileStream>( - stdin_pipe_.GetSelfFileDescriptor(), false, false, true, false); + stdin_pipe_.GetSelfFileDescriptor(), false, false, true, true); stdout_stream_ = std::make_unique<UnixFileStream>( - stdout_pipe_.GetSelfFileDescriptor(), false, true, false, false); + stdout_pipe_.GetSelfFileDescriptor(), false, true, false, true); stderr_stream_ = std::make_unique<UnixFileStream>( - stderr_pipe_.GetSelfFileDescriptor(), false, true, false, false); + stderr_pipe_.GetSelfFileDescriptor(), false, true, false, true); stdout_buffer_stream_ = std::make_unique<io::AutoReadStream>(stdout_stream_.get(), false); @@ -95,15 +95,35 @@ void PosixSpawnSubProcessImpl::PlatformCreateProcess( error = posix_spawn_file_actions_adddup2( &file_actions, stderr_pipe_.GetOtherFileDescriptor(), STDERR_FILENO); check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stderr."); + + error = posix_spawn_file_actions_addclose( + &file_actions, stdin_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd of stdin."); + error = posix_spawn_file_actions_addclose( + &file_actions, stdout_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd stdout."); + error = posix_spawn_file_actions_addclose( + &file_actions, stderr_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd stderr."); + error = posix_spawn_file_actions_addclose( &file_actions, stdin_pipe_.GetSelfFileDescriptor()); - check_error(u"Failed to call posix_spawn_file_actions_addclose on stdin."); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stdin."); error = posix_spawn_file_actions_addclose( &file_actions, stdout_pipe_.GetSelfFileDescriptor()); - check_error(u"Failed to call posix_spawn_file_actions_addclose on stdout."); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stdout."); error = posix_spawn_file_actions_addclose( &file_actions, stderr_pipe_.GetSelfFileDescriptor()); - check_error(u"Failed to call posix_spawn_file_actions_addclose on stderr."); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stderr."); posix_spawnattr_t attr; error = posix_spawnattr_init(&attr); @@ -130,11 +150,11 @@ void PosixSpawnSubProcessImpl::PlatformCreateProcess( check_error(u"Failed to call posix_spawnp."); error = ::close(stdin_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close stdin."); + check_error(u"Failed to close child stdin."); error = ::close(stdout_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close stdout."); + check_error(u"Failed to close child stdout."); error = ::close(stderr_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close stderr."); + check_error(u"Failed to close child stderr."); } SubProcessExitResult PosixSpawnSubProcessImpl::PlatformWaitForProcess() { diff --git a/src/common/platform/unix/UnixPipe.cpp b/src/common/platform/unix/UnixPipe.cpp index 4d081d5e..f30c599e 100644 --- a/src/common/platform/unix/UnixPipe.cpp +++ b/src/common/platform/unix/UnixPipe.cpp @@ -7,8 +7,8 @@ #include <unistd.h> namespace cru::platform::unix { -UnixPipe::UnixPipe(Usage usage, UnixPipeFlag flags) - : usage_(usage), flags_(flags) { +UnixPipe::UnixPipe(Usage usage, bool auto_close, UnixPipeFlag flags) + : usage_(usage), auto_close_(auto_close), flags_(flags) { int fds[2]; if (pipe(fds) != 0) { throw ErrnoException(u"Failed to create unix pipe."); @@ -40,8 +40,12 @@ int UnixPipe::GetOtherFileDescriptor() { } UnixPipe::~UnixPipe() { - if (close(GetSelfFileDescriptor()) != 0) { - CRU_LOG_ERROR(u"Failed to close unix pipe file descriptor."); + if (auto_close_) { + auto self_fd = GetSelfFileDescriptor(); + if (::close(self_fd) != 0) { + CRU_LOG_ERROR(u"Failed to close unix pipe file descriptor {}, errno {}.", + self_fd, errno); + } } } } // namespace cru::platform::unix diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index d2351b38..61222a68 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -10,6 +10,19 @@ add_executable(CruBaseTest ) target_link_libraries(CruBaseTest PRIVATE CruBase CruTestBase) +add_executable(CruTestHelperEcho + SubProcessHelper/CruEcho.cpp +) + +add_executable(CruTestHelperTee + SubProcessHelper/CruTee.cpp +) + +target_compile_definitions(CruBaseTest PRIVATE + CRU_TEST_HELPER_ECHO_LOCATION="$<TARGET_FILE:CruTestHelperEcho>" + CRU_TEST_HELPER_TEE_LOCATION="$<TARGET_FILE:CruTestHelperTee>" +) + if (UNIX AND NOT EMSCRIPTEN) target_sources(CruBaseTest PRIVATE platform/unix/UnixFileStreamTest.cpp diff --git a/test/common/StringToNumberConverterTest.cpp b/test/common/StringToNumberConverterTest.cpp index dc37bac0..d4bb36f4 100644 --- a/test/common/StringToNumberConverterTest.cpp +++ b/test/common/StringToNumberConverterTest.cpp @@ -5,79 +5,79 @@ TEST_CASE("StringToIntegerConverterImpl Base0", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter(0, 0); + StringToIntegerConverter converter({}, 0); Index processed_characters_count; REQUIRE(converter.Parse("12345678", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 12345678)); + StringToIntegerResult(false, 12345678)); REQUIRE(processed_characters_count == 8); REQUIRE(converter.Parse("0", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 1); REQUIRE(converter.Parse("012", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 012)); + StringToIntegerResult(false, 012)); REQUIRE(processed_characters_count == 3); REQUIRE(converter.Parse("0x12", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0x12)); + StringToIntegerResult(false, 0x12)); REQUIRE(processed_characters_count == 4); REQUIRE(converter.Parse("0X12", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0x12)); + StringToIntegerResult(false, 0x12)); REQUIRE(processed_characters_count == 4); REQUIRE(converter.Parse("0b101", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0b101)); + StringToIntegerResult(false, 0b101)); REQUIRE(processed_characters_count == 5); REQUIRE(converter.Parse("0B101", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0b101)); + StringToIntegerResult(false, 0b101)); REQUIRE(processed_characters_count == 5); REQUIRE(converter.Parse("-123", &processed_characters_count) == - StringToIntegerConverterImplResult(true, 123)); + StringToIntegerResult(true, 123)); REQUIRE(processed_characters_count == 4); REQUIRE(converter.Parse("-0x10", &processed_characters_count) == - StringToIntegerConverterImplResult(true, 0x10)); + StringToIntegerResult(true, 0x10)); REQUIRE(processed_characters_count == 5); } TEST_CASE("StringToIntegerConverterImpl Base0ForError", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter(0, 0); + StringToIntegerConverter converter({}, 0); Index processed_characters_count; REQUIRE(converter.Parse("a", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); REQUIRE(converter.Parse("0a", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); REQUIRE(converter.Parse("0x", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); REQUIRE(converter.Parse("0xx", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); REQUIRE(converter.Parse(" 0", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); REQUIRE(converter.Parse("0 ", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0)); + StringToIntegerResult(false, 0)); REQUIRE(processed_characters_count == 0); } TEST_CASE("StringToIntegerConverterImpl ThrowOnErrorFlag", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter(StringToNumberFlags::kThrowOnError, 0); + StringToIntegerConverter converter(StringToNumberFlags::kThrowOnError, 0); Index processed_characters_count; REQUIRE_THROWS_AS(converter.Parse("?", &processed_characters_count), Exception); @@ -85,56 +85,56 @@ TEST_CASE("StringToIntegerConverterImpl ThrowOnErrorFlag", "[string]") { TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroFlag", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter( + StringToIntegerConverter converter( StringToNumberFlags::kAllowLeadingSpaces, 0); Index processed_characters_count; REQUIRE(converter.Parse(" 123", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 123)); + StringToIntegerResult(false, 123)); REQUIRE(processed_characters_count == 6); } TEST_CASE("StringToIntegerConverterImpl AllowTrailingZeroFlag", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter( + StringToIntegerConverter converter( StringToNumberFlags::kAllowTrailingSpaces, 0); Index processed_characters_count; REQUIRE(converter.Parse("123 ", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 123)); + StringToIntegerResult(false, 123)); REQUIRE(processed_characters_count == 6); } TEST_CASE("StringToIntegerConverterImpl AllowTrailingJunk", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter( - StringToNumberFlags::kAllowLeadingZeroForInteger, 0); + StringToIntegerConverter converter( + StringToNumberFlags::kAllowTrailingJunk, 0); Index processed_characters_count; REQUIRE(converter.Parse("123 12", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 123)); + StringToIntegerResult(false, 123)); REQUIRE(processed_characters_count == 3); } TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroForInteger", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter( + StringToIntegerConverter converter( StringToNumberFlags::kAllowLeadingZeroForInteger, 0); Index processed_characters_count; REQUIRE(converter.Parse("0x0012", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0x12)); + StringToIntegerResult(false, 0x12)); REQUIRE(processed_characters_count == 6); REQUIRE(converter.Parse("000011", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 000011)); + StringToIntegerResult(false, 000011)); REQUIRE(processed_characters_count == 6); REQUIRE(converter.Parse("0b0011", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0b0011)); + StringToIntegerResult(false, 0b0011)); REQUIRE(processed_characters_count == 6); } TEST_CASE("StringToIntegerConverterImpl CompositeFlags", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter( + StringToIntegerConverter converter( StringToNumberFlags::kAllowLeadingSpaces | StringToNumberFlags::kAllowTrailingJunk | StringToNumberFlags::kAllowLeadingZeroForInteger, @@ -142,25 +142,25 @@ TEST_CASE("StringToIntegerConverterImpl CompositeFlags", "[string]") { Index processed_characters_count; REQUIRE(converter.Parse(" 0x00123!!!", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 0x00123)); + StringToIntegerResult(false, 0x00123)); REQUIRE(processed_characters_count == 10); } TEST_CASE("StringToIntegerConverterImpl OtherBase", "[string]") { using namespace cru; - StringToIntegerConverterImpl converter(0, 7); + StringToIntegerConverter converter({}, 7); Index processed_characters_count; REQUIRE(converter.Parse("12", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 9)); + StringToIntegerResult(false, 9)); REQUIRE(processed_characters_count == 2); REQUIRE(converter.Parse("-12", &processed_characters_count) == - StringToIntegerConverterImplResult(true, 9)); + StringToIntegerResult(true, 9)); REQUIRE(processed_characters_count == 3); converter.base = 11; REQUIRE(converter.Parse("1a", &processed_characters_count) == - StringToIntegerConverterImplResult(false, 21)); + StringToIntegerResult(false, 21)); REQUIRE(processed_characters_count == 2); } diff --git a/test/common/SubProcessHelper/CruEcho.cpp b/test/common/SubProcessHelper/CruEcho.cpp new file mode 100644 index 00000000..5f23c027 --- /dev/null +++ b/test/common/SubProcessHelper/CruEcho.cpp @@ -0,0 +1,9 @@ +#include <iostream> + +int main(int argc, char* argv[]) { + for (int i = 1; i < argc - 1; ++i) { + std::cout << argv[i] << " "; + } + std::cout << argv[argc - 1]; + return 0; +} diff --git a/test/common/SubProcessHelper/CruTee.cpp b/test/common/SubProcessHelper/CruTee.cpp new file mode 100644 index 00000000..4470d2a8 --- /dev/null +++ b/test/common/SubProcessHelper/CruTee.cpp @@ -0,0 +1,10 @@ +#include <iostream> +#include <string> + +int main() { + std::string s; + while (std::cin >> s) { + std::cout << s; + } + return 0; +} diff --git a/test/common/SubProcessTest.cpp b/test/common/SubProcessTest.cpp index e42ee943..03f9d221 100644 --- a/test/common/SubProcessTest.cpp +++ b/test/common/SubProcessTest.cpp @@ -1,15 +1,30 @@ +#include "cru/common/String.h" #include "cru/common/SubProcess.h" #include <catch2/catch_test_macros.hpp> +using cru::String; using cru::SubProcess; TEST_CASE("SubProcess", "[subprocess]") { - SECTION("should work.") { - SubProcess process = SubProcess::Create(u"echo", {u"abc"}); + SECTION("echo should work.") { + SubProcess process = SubProcess::Create( + String::FromUtf8(CRU_TEST_HELPER_ECHO_LOCATION), {u"abc"}); process.Wait(); REQUIRE(process.GetExitResult().IsSuccess()); auto output = process.GetStdoutStream()->ReadToEndAsUtf8String(); - REQUIRE(output == u"abc\n"); + REQUIRE(output == u"abc"); + } + + SECTION("tee should work.") { + constexpr auto str = "abc"; + SubProcess process = + SubProcess::Create(String::FromUtf8(CRU_TEST_HELPER_TEE_LOCATION)); + process.GetStdinStream()->Write(str, 3); + process.GetStdinStream()->Close(); + process.Wait(); + REQUIRE(process.GetExitResult().IsSuccess()); + auto output = process.GetStdoutStream()->ReadToEndAsUtf8String(); + REQUIRE(output == u"abc"); } } |