aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/common/Bitmask.h5
-rw-r--r--include/cru/common/Format.h59
-rw-r--r--include/cru/common/String.h32
-rw-r--r--include/cru/common/StringToNumberConverter.h88
-rw-r--r--include/cru/common/SubProcess.h2
-rw-r--r--include/cru/common/platform/unix/UnixPipe.h3
m---------lib/Catch20
m---------lib/double-conversion0
-rw-r--r--src/common/Format.cpp19
-rw-r--r--src/common/String.cpp36
-rw-r--r--src/common/StringToNumberConverter.cpp61
-rw-r--r--src/common/io/MemoryStream.cpp2
-rw-r--r--src/common/platform/unix/PosixSpawnSubProcess.cpp44
-rw-r--r--src/common/platform/unix/UnixPipe.cpp12
-rw-r--r--test/common/CMakeLists.txt13
-rw-r--r--test/common/StringToNumberConverterTest.cpp70
-rw-r--r--test/common/SubProcessHelper/CruEcho.cpp9
-rw-r--r--test/common/SubProcessHelper/CruTee.cpp10
-rw-r--r--test/common/SubProcessTest.cpp21
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");
}
}