aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2024-06-01 17:09:44 +0800
committercrupest <crupest@outlook.com>2024-06-08 17:01:55 +0800
commitd1f409530db6f9b712fd672c4c3154cac7eebad1 (patch)
tree1e48a3e2ff14e66aac2503a74df28cca7d85d02c
parentad796a167e33b54c7fa23ea21c73d57dba4fc928 (diff)
downloadcru-d1f409530db6f9b712fd672c4c3154cac7eebad1.tar.gz
cru-d1f409530db6f9b712fd672c4c3154cac7eebad1.tar.bz2
cru-d1f409530db6f9b712fd672c4c3154cac7eebad1.zip
HALF WORK: refactor something and implement part of subprocess.
-rw-r--r--include/cru/common/Buffer.h11
-rw-r--r--include/cru/common/Exception.h10
-rw-r--r--include/cru/common/Guard.h26
-rw-r--r--include/cru/common/String.h3
-rw-r--r--include/cru/common/StringUtil.h29
-rw-r--r--include/cru/common/SubProcess.h12
-rw-r--r--include/cru/common/io/Stream.h6
-rw-r--r--include/cru/common/platform/Exception.h3
-rw-r--r--include/cru/platform/Exception.h3
-rw-r--r--src/common/Buffer.cpp19
-rw-r--r--src/common/Exception.cpp3
-rw-r--r--src/common/String.cpp33
-rw-r--r--src/common/SubProcess.cpp11
-rw-r--r--src/common/platform/unix/PosixSpawnSubProcess.cpp88
14 files changed, 206 insertions, 51 deletions
diff --git a/include/cru/common/Buffer.h b/include/cru/common/Buffer.h
index 5c1f7ba3..a03b69eb 100644
--- a/include/cru/common/Buffer.h
+++ b/include/cru/common/Buffer.h
@@ -77,6 +77,8 @@ class Buffer final {
Index PushFront(const std::byte* other, Index other_size,
bool use_memmove = false);
+ bool PushBack(std::byte b);
+
/**
* @brief Append data to the back of used bytes and increase used size.
* @return The actual size of data saved.
@@ -129,6 +131,15 @@ class Buffer final {
operator std::byte*() { return GetPtr(); }
operator const std::byte*() const { return GetPtr(); }
+ /**
+ * @brief Detach internal buffer and return it.
+ * @param size If not null, size of the buffer is written to it.
+ * @return The buffer pointer. May be nullptr.
+ *
+ * After detach, you are responsible to delete[] it.
+ */
+ std::byte* Detach(Index* size = nullptr);
+
private:
void Copy_(const Buffer& other);
void Move_(Buffer&& other) noexcept;
diff --git a/include/cru/common/Exception.h b/include/cru/common/Exception.h
index 0aee871c..609fd2c9 100644
--- a/include/cru/common/Exception.h
+++ b/include/cru/common/Exception.h
@@ -1,6 +1,7 @@
#pragma once
#include "String.h"
+#include <exception>
#include <optional>
namespace cru {
@@ -9,16 +10,16 @@ namespace cru {
#endif
class CRU_BASE_API Exception : public std::exception {
public:
- explicit Exception(String message = {});
-
- CRU_DEFAULT_COPY(Exception)
- CRU_DEFAULT_MOVE(Exception)
+ explicit Exception(String message = {},
+ std::unique_ptr<std::exception> inner = nullptr);
~Exception() override;
public:
String GetMessage() const { return message_; }
+ std::exception* GetInner() const noexcept { return inner_.get(); }
+
const char* what() const noexcept override;
protected:
@@ -30,6 +31,7 @@ class CRU_BASE_API Exception : public std::exception {
private:
String message_;
mutable std::string utf8_message_;
+ std::unique_ptr<std::exception> inner_;
};
class CRU_BASE_API TextEncodeException : public Exception {
diff --git a/include/cru/common/Guard.h b/include/cru/common/Guard.h
new file mode 100644
index 00000000..5a9f9c5d
--- /dev/null
+++ b/include/cru/common/Guard.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <functional>
+
+namespace cru {
+struct Guard {
+ using ExitFunc = std::function<void()>;
+
+ Guard() = default;
+ explicit Guard(const ExitFunc& f) : on_exit(f) {}
+ explicit Guard(ExitFunc&& f) : on_exit(std::move(f)) {}
+ Guard(const Guard&) = delete;
+ Guard(Guard&&) = default;
+ Guard& operator=(const Guard&) = delete;
+ Guard& operator=(Guard&&) = default;
+ ~Guard() {
+ if (on_exit) {
+ on_exit();
+ }
+ }
+
+ void Drop() { on_exit = {}; }
+
+ ExitFunc on_exit;
+};
+} // namespace cru
diff --git a/include/cru/common/String.h b/include/cru/common/String.h
index c1ea839c..2156f060 100644
--- a/include/cru/common/String.h
+++ b/include/cru/common/String.h
@@ -1,6 +1,7 @@
#pragma once
#include "Base.h"
+#include "Buffer.h"
#include "Range.h"
#include "StringToNumberConverter.h"
#include "StringUtil.h"
@@ -241,6 +242,7 @@ class CRU_BASE_API String {
String Format(T&&... args) const;
std::string ToUtf8() const;
+ Buffer ToUtf8Buffer(bool end_zero = true) const;
int Compare(const String& other) const;
int CaseInsensitiveCompare(const String& other) const;
@@ -374,6 +376,7 @@ class CRU_BASE_API StringView {
std::vector<double> ParseToDoubleList(value_type separator = u' ') const;
std::string ToUtf8() const;
+ Buffer ToUtf8Buffer(bool end_zero = true) const;
private:
const char16_t* ptr_;
diff --git a/include/cru/common/StringUtil.h b/include/cru/common/StringUtil.h
index 19948250..8f085283 100644
--- a/include/cru/common/StringUtil.h
+++ b/include/cru/common/StringUtil.h
@@ -2,6 +2,7 @@
#include "Base.h"
#include <functional>
+#include <type_traits>
namespace cru {
using CodePoint = std::int32_t;
@@ -123,18 +124,19 @@ inline std::enable_if_t<std::is_unsigned_v<UInt>, ReturnType> ExtractBits(
}
} // namespace details
-template <typename TStr>
-bool Utf8EncodeCodePointAppend(CodePoint code_point, TStr& str) {
- auto write_continue_byte = [&str](std::uint8_t byte6) {
- str.push_back((1u << 7) + (((1u << 6) - 1) & byte6));
+template <typename CharWriter>
+std::enable_if_t<std::is_invocable_v<CharWriter, char>, bool>
+Utf8EncodeCodePointAppend(CodePoint code_point, CharWriter&& writer) {
+ auto write_continue_byte = [&writer](std::uint8_t byte6) {
+ writer((1u << 7) + (((1u << 6) - 1) & byte6));
};
if (code_point >= 0 && code_point <= 0x007F) {
- str.push_back(static_cast<char>(code_point));
+ writer(static_cast<char>(code_point));
return true;
} else if (code_point >= 0x0080 && code_point <= 0x07FF) {
std::uint32_t unsigned_code_point = code_point;
- str.push_back(
+ writer(
static_cast<char>(details::ExtractBits<std::uint32_t, 5, std::uint8_t>(
(unsigned_code_point >> 6)) +
0b11000000));
@@ -143,7 +145,7 @@ bool Utf8EncodeCodePointAppend(CodePoint code_point, TStr& str) {
return true;
} else if (code_point >= 0x0800 && code_point <= 0xFFFF) {
std::uint32_t unsigned_code_point = code_point;
- str.push_back(
+ writer(
static_cast<char>(details::ExtractBits<std::uint32_t, 4, std::uint8_t>(
(unsigned_code_point >> (6 * 2))) +
0b11100000));
@@ -154,7 +156,7 @@ bool Utf8EncodeCodePointAppend(CodePoint code_point, TStr& str) {
return true;
} else if (code_point >= 0x10000 && code_point <= 0x10FFFF) {
std::uint32_t unsigned_code_point = code_point;
- str.push_back(
+ writer(
static_cast<char>(details::ExtractBits<std::uint32_t, 3, std::uint8_t>(
(unsigned_code_point >> (6 * 3))) +
0b11110000));
@@ -170,18 +172,19 @@ bool Utf8EncodeCodePointAppend(CodePoint code_point, TStr& str) {
}
}
-template <typename TStr>
-bool Utf16EncodeCodePointAppend(CodePoint code_point, TStr& str) {
+template <typename CharWriter>
+std::enable_if_t<std::is_invocable_v<CharWriter, char16_t>, bool>
+Utf16EncodeCodePointAppend(CodePoint code_point, CharWriter&& writer) {
if ((code_point >= 0 && code_point <= 0xD7FF) ||
(code_point >= 0xE000 && code_point <= 0xFFFF)) {
- str.push_back(static_cast<char16_t>(code_point));
+ writer(static_cast<char16_t>(code_point));
return true;
} else if (code_point >= 0x10000 && code_point <= 0x10FFFF) {
std::uint32_t u = code_point - 0x10000;
- str.push_back(static_cast<char16_t>(
+ writer(static_cast<char16_t>(
details::ExtractBits<std::uint32_t, 10, std::uint32_t>(u >> 10) +
0xD800u));
- str.push_back(static_cast<char16_t>(
+ writer(static_cast<char16_t>(
details::ExtractBits<std::uint32_t, 10, std::uint32_t>(u) + 0xDC00u));
return true;
} else {
diff --git a/include/cru/common/SubProcess.h b/include/cru/common/SubProcess.h
index 8e5e49f5..ba46d049 100644
--- a/include/cru/common/SubProcess.h
+++ b/include/cru/common/SubProcess.h
@@ -9,6 +9,7 @@
#include <mutex>
#include <optional>
#include <thread>
+#include <unordered_map>
#include <vector>
namespace cru {
@@ -33,20 +34,19 @@ enum class PlatformSubProcessStatus {
class CRU_BASE_API SubProcessException : public Exception {
public:
- SubProcessException(String message = {});
- ~SubProcessException() override;
+ using Exception::Exception;
};
-class CRU_BASE_API SubProcessFailedToStartException : public Exception {
+class CRU_BASE_API SubProcessFailedToStartException
+ : public SubProcessException {
public:
- SubProcessFailedToStartException(String message = {});
- ~SubProcessFailedToStartException() override;
+ using SubProcessException::SubProcessException;
};
struct PlatformSubProcessStartInfo {
String program;
std::vector<String> arguments;
- std::vector<String> environments;
+ std::unordered_map<String, String> environments;
};
struct PlatformSubProcessExitResult {
diff --git a/include/cru/common/io/Stream.h b/include/cru/common/io/Stream.h
index b8e324d1..9f9807ae 100644
--- a/include/cru/common/io/Stream.h
+++ b/include/cru/common/io/Stream.h
@@ -13,9 +13,6 @@ class CRU_BASE_API StreamOperationNotSupportedException : public Exception {
public:
explicit StreamOperationNotSupportedException(String operation);
- CRU_DEFAULT_COPY(StreamOperationNotSupportedException)
- CRU_DEFAULT_MOVE(StreamOperationNotSupportedException)
-
CRU_DEFAULT_DESTRUCTOR(StreamOperationNotSupportedException)
public:
@@ -34,9 +31,6 @@ class CRU_BASE_API StreamAlreadyClosedException : public Exception {
public:
StreamAlreadyClosedException();
- CRU_DEFAULT_COPY(StreamAlreadyClosedException)
- CRU_DEFAULT_MOVE(StreamAlreadyClosedException)
-
CRU_DEFAULT_DESTRUCTOR(StreamAlreadyClosedException)
static void Check(bool closed);
diff --git a/include/cru/common/platform/Exception.h b/include/cru/common/platform/Exception.h
index c1b649f3..74dd6ad4 100644
--- a/include/cru/common/platform/Exception.h
+++ b/include/cru/common/platform/Exception.h
@@ -7,9 +7,6 @@ class CRU_BASE_API PlatformException : public Exception {
public:
using Exception::Exception; // inherit constructors
- CRU_DEFAULT_COPY(PlatformException)
- CRU_DEFAULT_MOVE(PlatformException)
-
CRU_DEFAULT_DESTRUCTOR(PlatformException)
};
} // namespace cru::platform
diff --git a/include/cru/platform/Exception.h b/include/cru/platform/Exception.h
index d6cda815..a957c95a 100644
--- a/include/cru/platform/Exception.h
+++ b/include/cru/platform/Exception.h
@@ -34,9 +34,6 @@ class CRU_PLATFORM_API ReuseException : public Exception {
public:
using Exception::Exception; // inherit constructors
- CRU_DEFAULT_COPY(ReuseException)
- CRU_DEFAULT_MOVE(ReuseException)
-
CRU_DEFAULT_DESTRUCTOR(ReuseException)
};
diff --git a/src/common/Buffer.cpp b/src/common/Buffer.cpp
index 67e67735..49a0f8ae 100644
--- a/src/common/Buffer.cpp
+++ b/src/common/Buffer.cpp
@@ -89,6 +89,15 @@ Index Buffer::PushFront(const std::byte* other, Index other_size,
return copy_size;
}
+bool Buffer::PushBack(std::byte b) {
+ if (IsUsedReachEnd()) {
+ return false;
+ }
+ ptr_[used_end_] = b;
+ used_end_++;
+ return true;
+}
+
Index Buffer::PushBack(const std::byte* other, Index other_size,
bool use_memmove) {
auto copy_size = std::min(size_ - used_end_, other_size);
@@ -138,6 +147,16 @@ Index Buffer::PopEnd(std::byte* buffer, Index size, bool use_memmove) {
return pop_size;
}
+std::byte* Buffer::Detach(Index* size) {
+ auto ptr = this->ptr_;
+ if (size) {
+ *size = this->size_;
+ }
+ this->ptr_ = nullptr;
+ this->size_ = this->used_begin_ = this->used_end_ = 0;
+ return ptr;
+}
+
void Buffer::Copy_(const Buffer& other) {
if (other.ptr_ == nullptr) {
ptr_ = nullptr;
diff --git a/src/common/Exception.cpp b/src/common/Exception.cpp
index 37fa0038..4110ad56 100644
--- a/src/common/Exception.cpp
+++ b/src/common/Exception.cpp
@@ -5,7 +5,8 @@
#include <cerrno>
namespace cru {
-Exception::Exception(String message) : message_(std::move(message)) {}
+Exception::Exception(String message, std::unique_ptr<std::exception> inner)
+ : message_(std::move(message)), inner_(std::move(inner)) {}
Exception::~Exception() {}
diff --git a/src/common/String.cpp b/src/common/String.cpp
index 0d1c38b8..b2e52e67 100644
--- a/src/common/String.cpp
+++ b/src/common/String.cpp
@@ -1,5 +1,6 @@
#include "cru/common/String.h"
+#include "cru/common/Buffer.h"
#include "cru/common/Exception.h"
#include "cru/common/StringToNumberConverter.h"
#include "cru/common/StringUtil.h"
@@ -31,7 +32,8 @@ String String::FromUtf8(const char* str, Index size) {
String result;
Utf8CodePointIterator iter(str, size);
for (auto cp : iter) {
- Utf16EncodeCodePointAppend(cp, result);
+ Utf16EncodeCodePointAppend(
+ cp, std::bind(&String::push_back, result, std::placeholders::_1));
}
return result;
}
@@ -260,6 +262,10 @@ bool String::EndWith(StringView str) const { return View().EndWith(str); }
std::string String::ToUtf8() const { return View().ToUtf8(); }
+Buffer String::ToUtf8Buffer(bool end_zero) const {
+ return View().ToUtf8Buffer();
+}
+
String& String::TrimStart() {
if (size_ == 0) return *this;
@@ -293,7 +299,9 @@ String& String::Trim() {
}
void String::AppendCodePoint(CodePoint code_point) {
- if (!Utf16EncodeCodePointAppend(code_point, *this)) {
+ if (!Utf16EncodeCodePointAppend(
+ code_point,
+ std::bind(&String::push_back, *this, std::placeholders::_1))) {
throw TextEncodeException(u"Code point out of range.");
}
}
@@ -523,11 +531,30 @@ Range StringView::RangeFromCodePointToCodeUnit(Range code_point_range) const {
std::string StringView::ToUtf8() const {
std::string result;
for (auto cp : CodePointIterator()) {
- Utf8EncodeCodePointAppend(cp, result);
+ Utf8EncodeCodePointAppend(
+ cp, std::bind(&std::string::push_back, result, std::placeholders::_1));
}
return result;
}
+Buffer StringView::ToUtf8Buffer(bool end_zero) const {
+ const Index grow_step = 10;
+ Buffer buffer(grow_step); // Maybe another init value is more reasonable.
+ auto push_back = [&buffer](char c) {
+ if (buffer.IsUsedReachEnd()) {
+ buffer.ResizeBuffer(buffer.GetBufferSize() + grow_step, true);
+ }
+ buffer.PushBack(static_cast<std::byte>(c));
+ };
+ for (auto cp : CodePointIterator()) {
+ Utf8EncodeCodePointAppend(cp, push_back);
+ }
+ if (end_zero) {
+ push_back(0);
+ }
+ return std::move(buffer);
+}
+
int StringView::ParseToInt(Index* processed_characters_count, unsigned flags,
int base) const {
return ParseToInteger<int>(processed_characters_count, flags, base);
diff --git a/src/common/SubProcess.cpp b/src/common/SubProcess.cpp
index 364cd66e..eea83acd 100644
--- a/src/common/SubProcess.cpp
+++ b/src/common/SubProcess.cpp
@@ -4,17 +4,6 @@
#include <mutex>
namespace cru {
-SubProcessException::SubProcessException(String message)
- : Exception(std::move(message)) {}
-
-SubProcessException::~SubProcessException() {}
-
-SubProcessFailedToStartException::SubProcessFailedToStartException(
- String message)
- : Exception(std::move(message)) {}
-
-SubProcessFailedToStartException::~SubProcessFailedToStartException() {}
-
PlatformSubProcessBase::PlatformSubProcessBase(
const PlatformSubProcessStartInfo& start_info)
: start_info_(start_info), process_lock_(process_mutex_, std::defer_lock) {}
diff --git a/src/common/platform/unix/PosixSpawnSubProcess.cpp b/src/common/platform/unix/PosixSpawnSubProcess.cpp
index 0eb5615d..8b9a9a47 100644
--- a/src/common/platform/unix/PosixSpawnSubProcess.cpp
+++ b/src/common/platform/unix/PosixSpawnSubProcess.cpp
@@ -1,8 +1,14 @@
#include "cru/common/platform/unix/PosixSpawnSubProcess.h"
+#include "cru/common/Exception.h"
+#include "cru/common/Format.h"
+#include "cru/common/Guard.h"
+#include "cru/common/String.h"
#include "cru/common/SubProcess.h"
#include <spawn.h>
+#include <unistd.h>
#include <memory>
+#include <unordered_map>
namespace cru::platform::unix {
PosixSpawnSubProcess::PosixSpawnSubProcess(
@@ -40,7 +46,87 @@ io::Stream* PosixSpawnSubProcess::GetStderrStream() {
return stderr_buffer_stream_.get();
}
-void PosixSpawnSubProcess::PlatformCreateProcess() {}
+namespace {
+char** CreateCstrArray(const std::vector<String>& argv) {
+ std::vector<Buffer> utf8_argv;
+ for (const auto& arg : argv) {
+ utf8_argv.push_back(arg.ToUtf8Buffer());
+ }
+ char** result = new char*[utf8_argv.size() + 1];
+ for (int i = 0; i < utf8_argv.size(); i++) {
+ result[i] = reinterpret_cast<char*>(utf8_argv[i].Detach());
+ }
+ result[utf8_argv.size()] = nullptr;
+ return result;
+}
+
+char** CreateCstrArray(const std::unordered_map<String, String>& envp) {
+ std::vector<String> str_array;
+ for (auto& [key, value] : envp) {
+ str_array.push_back(cru::Format(u"{}={}", key, value));
+ }
+ return CreateCstrArray(str_array);
+}
+
+void DestroyCstrArray(char** argv) {
+ int index = 0;
+ char* arg = argv[index];
+ while (arg) {
+ delete[] arg;
+ index++;
+ arg = argv[index];
+ }
+ delete[] argv;
+}
+} // namespace
+
+void PosixSpawnSubProcess::PlatformCreateProcess() {
+ int error;
+ auto check_error = [&error](String message) {
+ if (error == 0) return;
+ std::unique_ptr<ErrnoException> inner(new ErrnoException({}, error));
+ throw SubProcessFailedToStartException(std::move(message),
+ std::move(inner));
+ };
+
+ posix_spawn_file_actions_t file_actions;
+ error = posix_spawn_file_actions_init(&file_actions);
+ check_error(u"Failed to call posix_spawn_file_actions_init.");
+ Guard file_actions_guard(
+ [&file_actions] { posix_spawn_file_actions_destroy(&file_actions); });
+
+ // dup2 stdin/stdout/stderr
+ error = posix_spawn_file_actions_adddup2(
+ &file_actions, stdin_pipe_.GetOtherFileDescriptor(), STDIN_FILENO);
+ check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdin.");
+ error = posix_spawn_file_actions_adddup2(
+ &file_actions, stdout_pipe_.GetOtherFileDescriptor(), STDOUT_FILENO);
+ check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdout.");
+ 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.");
+
+ posix_spawnattr_t attr;
+ error = posix_spawnattr_init(&attr);
+ check_error(u"Failed to call posix_spawnattr_init.");
+ Guard attr_guard([&attr] { posix_spawnattr_destroy(&attr); });
+
+#ifdef CRU_PLATFORM_OSX
+ error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT);
+ check_error(u"Failed to set flag POSIX_SPAWN_CLOEXEC_DEFAULT (osx).");
+#endif
+
+ auto exe = start_info_.program.ToUtf8();
+
+ auto argv = CreateCstrArray(start_info_.arguments);
+ Guard argv_guard([argv] { DestroyCstrArray(argv); });
+
+ auto envp = CreateCstrArray(start_info_.environments);
+ Guard envp_guard([envp] { DestroyCstrArray(envp); });
+
+ error = posix_spawnp(&pid_, exe.c_str(), &file_actions, &attr, argv, envp);
+ check_error(u"Failed to call posix_spawnp.");
+}
PlatformSubProcessExitResult PosixSpawnSubProcess::PlatformWaitForProcess() {}