From 346239e144b0644ed2d1a6ec3dc9f29d1e802eae Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 16:57:08 +0800 Subject: import(life): Add cmake. --- works/life/computer-network-experiment/CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 works/life/computer-network-experiment/CMakeLists.txt (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt new file mode 100644 index 0000000..41fec71 --- /dev/null +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.20) + +set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake + CACHE STRING "Vcpkg toolchain file") + +project(network-experiment) + +set(CMAKE_CXX_STANDARD 17) + +add_executable(client client.cpp) + +add_executable(server server.cpp) +find_package(folly CONFIG REQUIRED) +target_link_libraries(server PRIVATE Folly::folly) -- cgit v1.2.3 From 6d8fecb163a9c813a1b533970997353d33b6bf5e Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 18:37:08 +0800 Subject: import(life): ... --- works/life/computer-network-experiment/Base.hpp | 59 ++++ .../computer-network-experiment/CMakeLists.txt | 7 +- .../life/computer-network-experiment/PreConfig.hpp | 6 + .../computer-network-experiment/StringUtil.cpp | 325 +++++++++++++++++++++ .../computer-network-experiment/StringUtil.hpp | 157 ++++++++++ works/life/computer-network-experiment/server.cpp | 77 ++++- 6 files changed, 615 insertions(+), 16 deletions(-) create mode 100644 works/life/computer-network-experiment/Base.hpp create mode 100644 works/life/computer-network-experiment/PreConfig.hpp create mode 100644 works/life/computer-network-experiment/StringUtil.cpp create mode 100644 works/life/computer-network-experiment/StringUtil.hpp (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/Base.hpp b/works/life/computer-network-experiment/Base.hpp new file mode 100644 index 0000000..b1ad55c --- /dev/null +++ b/works/life/computer-network-experiment/Base.hpp @@ -0,0 +1,59 @@ +#pragma once +#include "PreConfig.hpp" + +#include +#include +#include + +#define CRU_UNUSED(entity) static_cast(entity); + +#define CRU__CONCAT(a, b) a##b +#define CRU_MAKE_UNICODE_LITERAL(str) CRU__CONCAT(u, #str) + +#define CRU_DEFAULT_COPY(classname) \ + classname(const classname&) = default; \ + classname& operator=(const classname&) = default; + +#define CRU_DEFAULT_MOVE(classname) \ + classname(classname&&) = default; \ + classname& operator=(classname&&) = default; + +#define CRU_DELETE_COPY(classname) \ + classname(const classname&) = delete; \ + classname& operator=(const classname&) = delete; + +#define CRU_DELETE_MOVE(classname) \ + classname(classname&&) = delete; \ + classname& operator=(classname&&) = delete; + +namespace cru { +class Object { + public: + Object() = default; + CRU_DEFAULT_COPY(Object) + CRU_DEFAULT_MOVE(Object) + virtual ~Object() = default; +}; + +struct Interface { + Interface() = default; + CRU_DELETE_COPY(Interface) + CRU_DELETE_MOVE(Interface) + virtual ~Interface() = default; +}; + +[[noreturn]] inline void UnreachableCode() { std::terminate(); } + +using Index = gsl::index; + +// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine +template +inline void hash_combine(std::size_t& s, const T& v) { + std::hash h; + s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); +} + +#define CRU_DEFINE_CLASS_LOG_TAG(tag) \ + private: \ + constexpr static std::u16string_view log_tag = tag; +} // namespace cru diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 41fec71..923de1f 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -7,8 +7,13 @@ project(network-experiment) set(CMAKE_CXX_STANDARD 17) +find_package(fmt CONFIG REQUIRED) +find_package(Microsoft.GSL CONFIG REQUIRED) +add_library(base STATIC StringUtil.cpp) +target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) + add_executable(client client.cpp) add_executable(server server.cpp) find_package(folly CONFIG REQUIRED) -target_link_libraries(server PRIVATE Folly::folly) +target_link_libraries(server PRIVATE base Folly::folly) diff --git a/works/life/computer-network-experiment/PreConfig.hpp b/works/life/computer-network-experiment/PreConfig.hpp new file mode 100644 index 0000000..d78292c --- /dev/null +++ b/works/life/computer-network-experiment/PreConfig.hpp @@ -0,0 +1,6 @@ +#pragma once + +#ifdef _MSC_VER +// disable the unnecessary warning about multi-inheritance +#pragma warning(disable : 4250) +#endif diff --git a/works/life/computer-network-experiment/StringUtil.cpp b/works/life/computer-network-experiment/StringUtil.cpp new file mode 100644 index 0000000..1224bdc --- /dev/null +++ b/works/life/computer-network-experiment/StringUtil.cpp @@ -0,0 +1,325 @@ +#include "StringUtil.hpp" +#include "Base.hpp" +#include + +namespace cru { +namespace { +template +inline std::enable_if_t, ReturnType> ExtractBits( + UInt n) { + return static_cast(n & ((1u << number_of_bit) - 1)); +} +} // namespace + +CodePoint Utf8NextCodePoint(std::string_view str, Index current, + Index* next_position) { + CodePoint result; + + if (current >= static_cast(str.length())) { + result = k_invalid_code_point; + } else { + const auto cu0 = static_cast(str[current++]); + + auto read_next_folowing_code = [&str, ¤t]() -> CodePoint { + if (current == static_cast(str.length())) + throw TextEncodeException( + "Unexpected end when read continuing byte of multi-byte code " + "point."); + + const auto u = static_cast(str[current]); + if (!(u & (1u << 7)) || (u & (1u << 6))) { + throw TextEncodeException( + "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " + "multi-byte code point."); + } + + return ExtractBits(str[current++]); + }; + + if ((1u << 7) & cu0) { + if ((1u << 6) & cu0) { // 2~4-length code point + if ((1u << 5) & cu0) { // 3~4-length code point + if ((1u << 4) & cu0) { // 4-length code point + if (cu0 & (1u << 3)) { + throw TextEncodeException( + "Unexpected bad-format begin byte (not 0b11110xxx) of 4-byte" + "code point."); + } + + const CodePoint s0 = ExtractBits(cu0) + << (6 * 3); + const CodePoint s1 = read_next_folowing_code() << (6 * 2); + const CodePoint s2 = read_next_folowing_code() << 6; + const CodePoint s3 = read_next_folowing_code(); + result = s0 + s1 + s2 + s3; + } else { // 3-length code point + const CodePoint s0 = ExtractBits(cu0) + << (6 * 2); + const CodePoint s1 = read_next_folowing_code() << 6; + const CodePoint s2 = read_next_folowing_code(); + result = s0 + s1 + s2; + } + } else { // 2-length code point + const CodePoint s0 = ExtractBits(cu0) + << 6; + const CodePoint s1 = read_next_folowing_code(); + result = s0 + s1; + } + } else { + throw TextEncodeException( + "Unexpected bad-format (0b10xxxxxx) begin byte of a code point."); + } + } else { + result = static_cast(cu0); + } + } + + if (next_position != nullptr) *next_position = current; + return result; +} + +CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, + Index* next_position) { + CodePoint result; + + if (current >= static_cast(str.length())) { + result = k_invalid_code_point; + } else { + const auto cu0 = str[current++]; + + if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point + result = static_cast(cu0); + } else if (IsUtf16SurrogatePairLeading(cu0)) { // 2-length code point + if (current >= static_cast(str.length())) { + throw TextEncodeException( + "Unexpected end when reading second code unit of surrogate pair."); + } + const auto cu1 = str[current++]; + + if (!IsUtf16SurrogatePairTrailing(cu1)) { + throw TextEncodeException( + "Unexpected bad-range second code unit of surrogate pair."); + } + + const auto s0 = ExtractBits(cu0) << 10; + const auto s1 = ExtractBits(cu1); + + result = s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-range first code unit of surrogate pair."); + } + } + + if (next_position != nullptr) *next_position = current; + return result; +} + +CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, + Index* previous_position) { + CodePoint result; + if (current <= 0) { + result = k_invalid_code_point; + } else { + const auto cu0 = str[--current]; + + if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point + result = static_cast(cu0); + } else if (IsUtf16SurrogatePairTrailing(cu0)) { // 2-length code point + if (current <= 0) { + throw TextEncodeException( + "Unexpected end when reading first code unit of surrogate pair."); + } + const auto cu1 = str[--current]; + + if (!IsUtf16SurrogatePairLeading(cu1)) { + throw TextEncodeException( + "Unexpected bad-range first code unit of surrogate pair."); + } + + const auto s0 = ExtractBits(cu1) << 10; + const auto s1 = ExtractBits(cu0); + + result = s0 + s1 + 0x10000; + + } else { + throw TextEncodeException( + "Unexpected bad-range second code unit of surrogate pair."); + } + } + + if (previous_position != nullptr) *previous_position = current; + return result; +} + +void Utf8EncodeCodePointAppend(CodePoint code_point, std::string& str) { + auto write_continue_byte = [&str](std::uint8_t byte6) { + str.push_back((1u << 7) + (((1u << 6) - 1) & byte6)); + }; + + if (code_point >= 0 && code_point <= 0x007F) { + str.push_back(static_cast(code_point)); + } else if (code_point >= 0x0080 && code_point <= 0x07FF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> 6)) + + 0b11000000)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else if (code_point >= 0x0800 && code_point <= 0xFFFF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> (6 * 2))) + + 0b11100000)); + write_continue_byte( + ExtractBits(unsigned_code_point >> 6)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { + std::uint32_t unsigned_code_point = code_point; + str.push_back(static_cast(ExtractBits( + (unsigned_code_point >> (6 * 3))) + + 0b11110000)); + write_continue_byte(ExtractBits( + unsigned_code_point >> (6 * 2))); + write_continue_byte( + ExtractBits(unsigned_code_point >> 6)); + write_continue_byte( + ExtractBits(unsigned_code_point)); + } else { + throw TextEncodeException("Code point out of range."); + } +} + +void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str) { + if ((code_point >= 0 && code_point <= 0xD7FF) || + (code_point >= 0xE000 && code_point <= 0xFFFF)) { + str.push_back(static_cast(code_point)); + } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { + std::uint32_t u = code_point - 0x10000; + str.push_back(static_cast( + ExtractBits(u >> 10) + 0xD800u)); + str.push_back(static_cast( + ExtractBits(u) + 0xDC00u)); + } else { + throw TextEncodeException("Code point out of range."); + } +} + +std::string ToUtf8(std::u16string_view s) { + std::string result; + for (CodePoint cp : Utf16CodePointIterator{s}) { + Utf8EncodeCodePointAppend(cp, result); + } + return result; +} + +std::u16string ToUtf16(std::string_view s) { + std::u16string result; + for (CodePoint cp : Utf8CodePointIterator{s}) { + Utf16EncodeCodePointAppend(cp, result); + } + return result; +} + +#ifdef WIN32 +std::wstring ToUtf16WString(std::string_view s) { + std::u16string result; + for (CodePoint cp : Utf8CodePointIterator{s}) { + Utf16EncodeCodePointAppend(cp, result); + } + + std::wstring r(result.cbegin(), result.cend()); + return r; +} +#endif + +bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position) { + if (position < 0) return false; + if (position > static_cast(s.size())) return false; + if (position == 0) return true; + if (position == static_cast(s.size())) return true; + return !IsUtf16SurrogatePairTrailing(s[position]); +} + +gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate) { + if (position <= 0) return position; + while (true) { + gsl::index p = position; + auto c = Utf16PreviousCodePoint(str, p, &position); + if (predicate(c)) return p; + if (c == k_invalid_code_point) return p; + } + UnreachableCode(); +} + +gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate) { + if (position >= static_cast(str.size())) return position; + while (true) { + gsl::index p = position; + auto c = Utf16NextCodePoint(str, p, &position); + if (predicate(c)) return p; + if (c == k_invalid_code_point) return p; + } + UnreachableCode(); +} + +inline bool IsSpace(CodePoint c) { return c == 0x20 || c == 0xA; } + +gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, + bool* is_space) { + if (position <= 0) return position; + auto c = Utf16PreviousCodePoint(str, position, nullptr); + if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). + if (is_space) *is_space = true; + return Utf16BackwardUntil(str, position, + [](CodePoint c) { return !IsSpace(c); }); + } else { + if (is_space) *is_space = false; + return Utf16BackwardUntil(str, position, IsSpace); + } +} + +gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, + bool* is_space) { + if (position >= static_cast(str.size())) return position; + auto c = Utf16NextCodePoint(str, position, nullptr); + if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). + if (is_space) *is_space = true; + return Utf16ForwardUntil(str, position, + [](CodePoint c) { return !IsSpace(c); }); + } else { + if (is_space) *is_space = false; + return Utf16ForwardUntil(str, position, IsSpace); + } +} + +char16_t ToLower(char16_t c) { + if (c >= u'A' && c <= u'Z') { + return c - u'A' + u'a'; + } + return c; +} + +char16_t ToUpper(char16_t c) { + if (c >= u'a' && c <= u'z') { + return c - u'a' + u'A'; + } + return c; +} + +std::u16string ToLower(std::u16string_view s) { + std::u16string result; + for (auto c : s) result.push_back(ToLower(c)); + return result; +} + +std::u16string ToUpper(std::u16string_view s) { + std::u16string result; + for (auto c : s) result.push_back(ToUpper(c)); + return result; +} +} // namespace cru diff --git a/works/life/computer-network-experiment/StringUtil.hpp b/works/life/computer-network-experiment/StringUtil.hpp new file mode 100644 index 0000000..1a9634a --- /dev/null +++ b/works/life/computer-network-experiment/StringUtil.hpp @@ -0,0 +1,157 @@ +#pragma once +#include "Base.hpp" + +#include +#include +#include + +namespace cru { +using CodePoint = std::int32_t; +constexpr CodePoint k_invalid_code_point = -1; + +class TextEncodeException : public std::runtime_error { + public: + using runtime_error::runtime_error; +}; + +inline bool IsUtf16SurrogatePairCodeUnit(char16_t c) { + return c >= 0xD800 && c <= 0xDFFF; +} + +inline bool IsUtf16SurrogatePairLeading(char16_t c) { + return c >= 0xD800 && c <= 0xDBFF; +} + +inline bool IsUtf16SurrogatePairTrailing(char16_t c) { + return c >= 0xDC00 && c <= 0xDFFF; +} + +CodePoint Utf8NextCodePoint(std::string_view str, Index current, + Index* next_position); + +CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, + Index* next_position); +CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, + Index* previous_position); + +template +using NextCodePointFunctionType = CodePoint (*)(StringType, Index, Index*); + +template NextCodePointFunction> +class CodePointIterator { + public: + using difference_type = Index; + using value_type = CodePoint; + using pointer = void; + using reference = value_type; + using iterator_category = std::forward_iterator_tag; + + public: + struct past_end_tag_t {}; + + explicit CodePointIterator(StringType string) + : string_(std::move(string)), position_(0) {} + explicit CodePointIterator(StringType string, past_end_tag_t) + : string_(std::move(string)), position_(string_.size()) {} + + CRU_DEFAULT_COPY(CodePointIterator) + CRU_DEFAULT_MOVE(CodePointIterator) + + ~CodePointIterator() = default; + + public: + StringType GetString() const { return string_; } + Index GetPosition() const { return position_; } + + bool IsPastEnd() const { + return position_ == static_cast(string_.size()); + } + + public: + CodePointIterator begin() const { return *this; } + CodePointIterator end() const { + return CodePointIterator{string_, past_end_tag_t{}}; + } + + public: + bool operator==(const CodePointIterator& other) const { + // You should compare iterator that iterate on the same string. + Expects(this->string_.data() == other.string_.data() && + this->string_.size() == other.string_.size()); + return this->position_ == other.position_; + } + bool operator!=(const CodePointIterator& other) const { + return !this->operator==(other); + } + + CodePointIterator& operator++() { + Expects(!IsPastEnd()); + Forward(); + return *this; + } + + CodePointIterator operator++(int) { + Expects(!IsPastEnd()); + CodePointIterator old = *this; + Forward(); + return old; + } + + CodePoint operator*() const { + return NextCodePointFunction(string_, position_, &next_position_cache_); + } + + private: + void Forward() { + if (next_position_cache_ > position_) { + position_ = next_position_cache_; + } else { + NextCodePointFunction(string_, position_, &position_); + } + } + + private: + StringType string_; + Index position_; + mutable Index next_position_cache_; +}; + +using Utf8CodePointIterator = + CodePointIterator; + +using Utf16CodePointIterator = + CodePointIterator; + +void Utf8EncodeCodePointAppend(CodePoint code_point, std::string& str); +void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str); + +std::string ToUtf8(std::u16string_view s); +std::u16string ToUtf16(std::string_view s); + +#ifdef WIN32 +std::wstring ToUtf16WString(std::string_view s); +#endif + +// If given s is not a valid utf16 string, return value is UD. +bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position); + +// Return position after the character making predicate returns true or 0 if no +// character doing so. +gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate); +// Return position before the character making predicate returns true or +// str.size() if no character doing so. +gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, + const std::function& predicate); + +gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); +gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, + bool* is_space = nullptr); + +char16_t ToLower(char16_t c); +char16_t ToUpper(char16_t c); +std::u16string ToLower(std::u16string_view s); +std::u16string ToUpper(std::u16string_view s); +} // namespace cru diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 15470f4..de5bcd3 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -3,30 +3,83 @@ */ #include -#include #include -#include #include #include #include #include +#include +#include +#include + #include #include +#include "StringUtil.hpp" +#include "fmt/core.h" + #pragma comment(lib, "Ws2_32.lib") const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port -// As far as I know, cout is not thread safe. So we need a lock. But this might -// not be the best solution. We can use a queue instead to avoid block. -std::mutex cout_mutex; +enum class OutputType { Normal, Error }; + +struct Output { + Output() = default; + Output(std::wstring message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + std::wstring message; + OutputType type; +}; + +folly::MPMCQueue output_queue; + +void SendOutput(std::wstring output) { + output_queue.blockingWrite(std::move(output)); +} + +void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +template +void SendOutput(std::wstring_view format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { + output_queue.blockingWrite( + {fmt::format(format, std::forward(args)...), type}); +} + +void OutputThread() { + while (true) { + Output output; + output_queue.blockingRead(output); + switch (output.type) { + case OutputType::Error: + std::wcerr << output.message; + break; + default: + std::wcout << output.message; + break; + } + } +} [[noreturn]] void PrintErrorMessageAndExit(std::wstring_view message, std::optional error_code = std::nullopt) { - std::wcerr << message << L'\n'; + + SendOutput(L"{}\n", message); if (error_code) { std::cerr << L"Error code is " << std::hex << *error_code << L'\n'; @@ -48,10 +101,7 @@ PrintErrorMessageAndExit(std::wstring_view message, void ResponseThreadProc(int socket, sockaddr_in address) { auto address_string = inet_ntoa(address.sin_addr); - { - std::lock_guard guard(cout_mutex); - std::cout << "Connected to " << address_string << "!\n"; - } + SendOutput(L"Connected to {}!\n", cru::ToUtf16WString(address_string)); const std::string_view buffer = "Love you!!! By crupest!"; @@ -69,7 +119,6 @@ void ResponseThreadProc(int socket, sockaddr_in address) { // send failed if (byte_actually_sent == SOCKET_ERROR) { - std::lock_guard guard(cout_mutex); std::cerr << "Failed to send!\n"; closesocket(socket); break; @@ -78,10 +127,8 @@ void ResponseThreadProc(int socket, sockaddr_in address) { byte_count_sent += byte_actually_sent; } - { - std::lock_guard guard(cout_mutex); - std::cout << "Succeeded to send message to " << address_string << "!\n"; - } + SendOutput(L"Succeeded to send message to {} !\n", + cru::ToUtf16WString(address_string)); closesocket(socket); } -- cgit v1.2.3 From ff92a987ad05e40a1315306b31bbc4a219d2ee1d Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 21:33:52 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Output.cpp | 18 ++++++ works/life/computer-network-experiment/Output.h | 46 +++++++++++++++ works/life/computer-network-experiment/server.cpp | 65 ++-------------------- 4 files changed, 70 insertions(+), 61 deletions(-) create mode 100644 works/life/computer-network-experiment/Output.cpp create mode 100644 works/life/computer-network-experiment/Output.h (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 923de1f..ecd8764 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC StringUtil.cpp) +add_library(base STATIC StringUtil.cpp Output.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) add_executable(client client.cpp) diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp new file mode 100644 index 0000000..41e14e6 --- /dev/null +++ b/works/life/computer-network-experiment/Output.cpp @@ -0,0 +1,18 @@ +#include "Output.h" + +folly::MPMCQueue output_queue; + +void OutputThread() { + while (true) { + Output output; + output_queue.blockingRead(output); + switch (output.type) { + case OutputType::Error: + std::wcerr << output.message; + break; + default: + std::wcout << output.message; + break; + } + } +} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h new file mode 100644 index 0000000..0e53363 --- /dev/null +++ b/works/life/computer-network-experiment/Output.h @@ -0,0 +1,46 @@ +#pragma once +#include "StringUtil.hpp" + +#include + +#include +#include +#include + +enum class OutputType { Normal, Error }; + +struct Output { + Output() = default; + Output(std::wstring message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + std::wstring message; + OutputType type; +}; + +extern folly::MPMCQueue output_queue; + +inline void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +inline void SendOutput(std::wstring output) { + SendOutput(std::move(output)); +} + +template +void SendOutput(std::wstring_view format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), type}); +} + +void OutputThread(); diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index de5bcd3..4942bb5 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -2,6 +2,8 @@ * This is the server program. */ +#include "Output.h" + #include #include #include @@ -9,71 +11,14 @@ #include #include -#include -#include -#include - #include #include -#include "StringUtil.hpp" -#include "fmt/core.h" - #pragma comment(lib, "Ws2_32.lib") const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port -enum class OutputType { Normal, Error }; - -struct Output { - Output() = default; - Output(std::wstring message, OutputType type = OutputType::Normal) - : message(std::move(message)), type(type) {} - - CRU_DEFAULT_COPY(Output) - CRU_DEFAULT_MOVE(Output) - ~Output() = default; - - std::wstring message; - OutputType type; -}; - -folly::MPMCQueue output_queue; - -void SendOutput(std::wstring output) { - output_queue.blockingWrite(std::move(output)); -} - -void SendOutput(Output output) { - output_queue.blockingWrite(std::move(output)); -} - -template -void SendOutput(std::wstring_view format, Args &&...args) { - output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); -} - -template -void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { - output_queue.blockingWrite( - {fmt::format(format, std::forward(args)...), type}); -} - -void OutputThread() { - while (true) { - Output output; - output_queue.blockingRead(output); - switch (output.type) { - case OutputType::Error: - std::wcerr << output.message; - break; - default: - std::wcout << output.message; - break; - } - } -} [[noreturn]] void PrintErrorMessageAndExit(std::wstring_view message, @@ -82,15 +27,15 @@ PrintErrorMessageAndExit(std::wstring_view message, SendOutput(L"{}\n", message); if (error_code) { - std::cerr << L"Error code is " << std::hex << *error_code << L'\n'; + SendOutput(OutputType::Error, L"Error code is {}.\n", *error_code); wchar_t buffer[500]; if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, *error_code, 0, buffer, 500, nullptr)) { - std::wcerr << L"Failed to format error message.\n"; + SendOutput(OutputType::Error, L"Failed to format error message.\n"); } else { - std::wcerr << buffer << L'\n'; + SendOutput(OutputType::Error, L"{}\n", buffer); } } -- cgit v1.2.3 From f0309ee1e5cd268091f59f3aa377beca77d76c5c Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 22:05:28 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Common.cpp | 48 ++++++++++++++++++++ works/life/computer-network-experiment/Common.h | 37 +++++++++++++++ works/life/computer-network-experiment/Output.cpp | 4 +- works/life/computer-network-experiment/Output.h | 17 ++++--- works/life/computer-network-experiment/server.cpp | 52 +++++----------------- 6 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 works/life/computer-network-experiment/Common.cpp create mode 100644 works/life/computer-network-experiment/Common.h (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index ecd8764..dc82736 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC StringUtil.cpp Output.cpp) +add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) add_executable(client client.cpp) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp new file mode 100644 index 0000000..02ac550 --- /dev/null +++ b/works/life/computer-network-experiment/Common.cpp @@ -0,0 +1,48 @@ +#include "Common.h" + +#include "Output.h" + +#ifdef WIN32 +#include +#include +#pragma comment(lib, "Ws2_32.lib") +#endif + +[[noreturn]] void PrintErrorMessageAndExit(StringView message, + bool print_last_error) { + + SendOutput(CRUT("{}\n"), message); + + if (print_last_error) { +#ifdef WIN32 + auto error_code = WSAGetLastError(); + SendOutput(OutputType::Error, CRUT("Error code is {}.\n"), error_code); + wchar_t buffer[500]; + if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, 0, buffer, 500, nullptr)) { + SendOutput(OutputType::Error, CRUT("Failed to format error message.\n")); + } else { + SendOutput(OutputType::Error, CRUT("{}\n"), buffer); + } +#else +#endif + } + +#ifdef WIN32 + WSACleanup(); +#endif + + std::exit(1); +} + +#ifdef WIN32 +void InitWSA() { + WSADATA wsa_data; + + if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa + PrintErrorMessageAndExit(CRUT("Failed to initialize wsa.")); + } +} +#endif diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h new file mode 100644 index 0000000..1f4fa23 --- /dev/null +++ b/works/life/computer-network-experiment/Common.h @@ -0,0 +1,37 @@ +#pragma once +#include "StringUtil.hpp" + +#include +#include +#include + +#ifdef WIN32 +using Char = wchar_t; +using String = std::wstring; +using StringView = std::wstring_view; +inline auto &input_stream = std::wcin; +inline auto &output_stream = std::wcout; +inline auto &error_stream = std::wcerr; +#define CRUT(string_literal) L##string_literal + +inline String ConvertCharString(std::string_view s) { + return cru::ToUtf16WString(s); +} +#else +using Char = char; +using String = std::string; +using StringView = std::string_view; +inline auto &input_stream = std::cin; +inline auto &output_stream = std::cout; +inline auto &error_stream = std::cerr; +#define CRUT(string_literal) string_literal + +inline String ConvertCharString(std::string_view s) { return s; } +#endif + +[[noreturn]] void PrintErrorMessageAndExit(StringView message, + bool print_last_error = true); + +#ifdef WIN32 +void InitWSA(); +#endif diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp index 41e14e6..ac4a47b 100644 --- a/works/life/computer-network-experiment/Output.cpp +++ b/works/life/computer-network-experiment/Output.cpp @@ -8,10 +8,10 @@ void OutputThread() { output_queue.blockingRead(output); switch (output.type) { case OutputType::Error: - std::wcerr << output.message; + error_stream << output.message; break; default: - std::wcout << output.message; + output_stream << output.message; break; } } diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h index 0e53363..157e5c8 100644 --- a/works/life/computer-network-experiment/Output.h +++ b/works/life/computer-network-experiment/Output.h @@ -1,24 +1,25 @@ #pragma once +#include "Common.h" #include "StringUtil.hpp" -#include - #include #include #include +#include + enum class OutputType { Normal, Error }; struct Output { Output() = default; - Output(std::wstring message, OutputType type = OutputType::Normal) + Output(String message, OutputType type = OutputType::Normal) : message(std::move(message)), type(type) {} CRU_DEFAULT_COPY(Output) CRU_DEFAULT_MOVE(Output) ~Output() = default; - std::wstring message; + String message; OutputType type; }; @@ -28,17 +29,15 @@ inline void SendOutput(Output output) { output_queue.blockingWrite(std::move(output)); } -inline void SendOutput(std::wstring output) { - SendOutput(std::move(output)); -} +inline void SendOutput(String output) { SendOutput(std::move(output)); } template -void SendOutput(std::wstring_view format, Args &&...args) { +void SendOutput(StringView format, Args &&...args) { output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); } template -void SendOutput(OutputType type, std::wstring_view format, Args &&...args) { +void SendOutput(OutputType type, StringView format, Args &&...args) { output_queue.blockingWrite( Output{fmt::format(format, std::forward(args)...), type}); } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 4942bb5..297114b 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -11,42 +11,12 @@ #include #include -#include -#include - -#pragma comment(lib, "Ws2_32.lib") - const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port - -[[noreturn]] void -PrintErrorMessageAndExit(std::wstring_view message, - std::optional error_code = std::nullopt) { - - SendOutput(L"{}\n", message); - - if (error_code) { - SendOutput(OutputType::Error, L"Error code is {}.\n", *error_code); - wchar_t buffer[500]; - if (!FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ARGUMENT_ARRAY | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, *error_code, 0, buffer, 500, nullptr)) { - SendOutput(OutputType::Error, L"Failed to format error message.\n"); - } else { - SendOutput(OutputType::Error, L"{}\n", buffer); - } - } - - WSACleanup(); - - std::exit(1); -} - void ResponseThreadProc(int socket, sockaddr_in address) { auto address_string = inet_ntoa(address.sin_addr); - SendOutput(L"Connected to {}!\n", cru::ToUtf16WString(address_string)); + SendOutput(CRUT("Connected to {}!\n"), ConvertCharString(address_string)); const std::string_view buffer = "Love you!!! By crupest!"; @@ -72,23 +42,21 @@ void ResponseThreadProc(int socket, sockaddr_in address) { byte_count_sent += byte_actually_sent; } - SendOutput(L"Succeeded to send message to {} !\n", - cru::ToUtf16WString(address_string)); + SendOutput(CRUT("Succeeded to send message to {} !\n"), + ConvertCharString(address_string)); closesocket(socket); } int main() { - WSADATA wsa_data; - - if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa - PrintErrorMessageAndExit(L"Failed to initialize wsa.", WSAGetLastError()); - } +#ifdef WIN32 + InitWSA(); +#endif int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - PrintErrorMessageAndExit(L"Failed to create socket.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to create socket.")); } sockaddr_in server_address; @@ -100,11 +68,11 @@ int main() { if (bind(server_socket, reinterpret_cast(&server_address), sizeof(sockaddr_in)) == SOCKET_ERROR) { - PrintErrorMessageAndExit(L"Failed to bind.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to bind.")); } if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) { - PrintErrorMessageAndExit(L"Failed to listen.", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to listen.")); } while (true) { @@ -116,7 +84,7 @@ int main() { &sin_size); if (client_socket == INVALID_SOCKET) { - PrintErrorMessageAndExit(L"Failed to accecpt", WSAGetLastError()); + PrintErrorMessageAndExit(CRUT("Failed to accecpt")); } std::thread response_thread(ResponseThreadProc, client_socket, -- cgit v1.2.3 From fcd2304e04dd3fabdefce4293b1b76ad99f8ed73 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 6 Jun 2021 22:13:43 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 8 +++-- works/life/computer-network-experiment/Common.cpp | 15 ++++++++- works/life/computer-network-experiment/Common.h | 2 ++ works/life/computer-network-experiment/client.cpp | 38 +++++++--------------- works/life/computer-network-experiment/server.cpp | 16 ++++----- 5 files changed, 40 insertions(+), 39 deletions(-) (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index dc82736..4d4277c 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -10,10 +10,14 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) -target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt) +target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt Folly::folly) +if(WIN32) +target_link_libraries(base PUBLIC Ws2_32) +endif() add_executable(client client.cpp) +target_link_libraries(client PRIVATE base) add_executable(server server.cpp) find_package(folly CONFIG REQUIRED) -target_link_libraries(server PRIVATE base Folly::folly) +target_link_libraries(server PRIVATE base) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index 02ac550..2e01dcb 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -5,7 +5,6 @@ #ifdef WIN32 #include #include -#pragma comment(lib, "Ws2_32.lib") #endif [[noreturn]] void PrintErrorMessageAndExit(StringView message, @@ -46,3 +45,17 @@ void InitWSA() { } } #endif + +int main() { +#ifdef WIN32 + InitWSA(); +#endif + + int c = Main(); + +#ifdef WIN32 + WSACleanup(); +#endif + + return c; +} diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 1f4fa23..45a7da1 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -14,6 +14,8 @@ inline auto &output_stream = std::wcout; inline auto &error_stream = std::wcerr; #define CRUT(string_literal) L##string_literal +int Main(); + inline String ConvertCharString(std::string_view s) { return cru::ToUtf16WString(s); } diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index 926ece6..aeb352f 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -2,32 +2,22 @@ * This is the client program. */ +#include "Common.h" +#include "Output.h" + +#ifdef WIN32 #include -#include -#include #include - -#pragma comment(lib, "ws2_32.lib") +#endif const auto connect_address = "127.0.0.1"; // control connect address const u_short port = 1234; // control connect port -int main() { - WSADATA wsa_data; - - if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) // initialize wsa - { - std::cerr << "WSA start up error!\n"; - WSACleanup(); - return 1; - } - +int Main() { int client_socket; if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - std::cerr << "Failed to create socket!\n"; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to create socket!\n")); } sockaddr_in server_address; @@ -39,28 +29,22 @@ int main() { if (connect(client_socket, (sockaddr *)&server_address, sizeof(sockaddr)) == SOCKET_ERROR) { - std::cerr << "Failed to connect!"; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to connect!")); } const int buffer_size = 100; - char * buffer = new char[buffer_size]; + char *buffer = new char[buffer_size]; int received_number = recv(client_socket, buffer, buffer_size, 0); if (received_number == -1) { - std::cerr << "Failed to recv."; - WSACleanup(); - return 1; + PrintErrorMessageAndExit(CRUT("Failed to recv.")); } std::string s(buffer, received_number); - std::cout << "Received message:\n" << s; + SendOutput(CRUT("Received message:\n")); closesocket(client_socket); - WSACleanup(); - return 0; } diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 297114b..9cf655a 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -2,15 +2,17 @@ * This is the server program. */ +#include "Common.h" #include "Output.h" -#include -#include #include -#include -#include #include +#ifdef WIN32 +#include +#include +#endif + const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port @@ -48,11 +50,7 @@ void ResponseThreadProc(int socket, sockaddr_in address) { closesocket(socket); } -int main() { -#ifdef WIN32 - InitWSA(); -#endif - +int Main() { int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { -- cgit v1.2.3 From 9c42586bc406015be0145576fd6cb3586686b4ca Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 7 Jun 2021 23:24:43 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- works/life/computer-network-experiment/Common.cpp | 8 +-- works/life/computer-network-experiment/Common.h | 5 +- works/life/computer-network-experiment/IO.cpp | 80 ++++++++++++++++++++++ works/life/computer-network-experiment/IO.h | 66 ++++++++++++++++++ works/life/computer-network-experiment/Output.cpp | 76 -------------------- works/life/computer-network-experiment/Output.h | 67 ------------------ works/life/computer-network-experiment/client.cpp | 2 +- works/life/computer-network-experiment/server.cpp | 24 +++++-- 9 files changed, 172 insertions(+), 158 deletions(-) create mode 100644 works/life/computer-network-experiment/IO.cpp create mode 100644 works/life/computer-network-experiment/IO.h delete mode 100644 works/life/computer-network-experiment/Output.cpp delete mode 100644 works/life/computer-network-experiment/Output.h (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 4d4277c..09c1ea4 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC Common.cpp StringUtil.cpp Output.cpp) +add_library(base STATIC Common.cpp StringUtil.cpp IO.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt Folly::folly) if(WIN32) target_link_libraries(base PUBLIC Ws2_32) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp index e4615fc..ec6fd1a 100644 --- a/works/life/computer-network-experiment/Common.cpp +++ b/works/life/computer-network-experiment/Common.cpp @@ -1,6 +1,6 @@ #include "Common.h" -#include "Output.h" +#include "IO.h" #ifdef WIN32 #include @@ -70,12 +70,6 @@ void BeforeExit() { SignalAndWaitForOutputThreadStop(); } -String ReadInputLine() { - String line; - std::getline(input_stream, line); - return line; -} - void SafeSend(int socket, std::string_view buffer) { const int total_byte_count = buffer.size(); int byte_count_sent = 0; diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h index 6886e38..d2f2e52 100644 --- a/works/life/computer-network-experiment/Common.h +++ b/works/life/computer-network-experiment/Common.h @@ -2,6 +2,7 @@ #include "StringUtil.hpp" #include +#include #include #include @@ -9,6 +10,7 @@ using Char = wchar_t; using String = std::wstring; using StringView = std::wstring_view; +using StringStream = std::wstringstream; inline auto &input_stream = std::wcin; inline auto &output_stream = std::wcout; inline auto &error_stream = std::wcerr; @@ -25,6 +27,7 @@ inline std::string ConvertCharStringBack(StringView s) { using Char = char; using String = std::string; using StringView = std::string_view; +using StringStream = std::stringstream; inline auto &input_stream = std::cin; inline auto &output_stream = std::cout; inline auto &error_stream = std::cerr; @@ -43,7 +46,5 @@ int CloseSocket(int socket); void BeforeExit(); -String ReadInputLine(); - void SafeSend(int socket, std::string_view buffer); std::string SafeReadUntil(int socket, char c, std::string &rest); diff --git a/works/life/computer-network-experiment/IO.cpp b/works/life/computer-network-experiment/IO.cpp new file mode 100644 index 0000000..0c20537 --- /dev/null +++ b/works/life/computer-network-experiment/IO.cpp @@ -0,0 +1,80 @@ +#include "IO.h" + +#include + +#include +#include +#include +#include + +folly::MPMCQueue output_queue(100); + +folly::CancellationSource cancellation_source; + +std::thread io_thread; + +void PrintOutput(const Output &output) { + std::basic_ostream *stream; + + switch (output.type) { + case OutputType::Error: + stream = &error_stream; + break; + default: + stream = &output_stream; + break; + } + + switch (output.color) { + case OutputColor::Normal: + (*stream) << output.message; + break; + case OutputColor::Green: + (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + case OutputColor::Red: + (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + case OutputColor::Yellow: + (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[39m") + << std::flush; + break; + } +} + +String ReadInputLine() { + String line; + std::getline(input_stream, line); + return line; +} + +void IOThread() { + while (true) { + if (cancellation_source.isCancellationRequested()) { + while (true) { + Output output; + if (output_queue.readIfNotEmpty(output)) { + PrintOutput(output); + } else { + return; + } + } + } + + Output output; + while (output_queue.readIfNotEmpty(output)) + PrintOutput(output); + + PrintOutput({CRUT("> ")}); + OnInputLine(ReadInputLine()); + } +} + +void SignalAndWaitForOutputThreadStop() { + cancellation_source.requestCancellation(); + io_thread.join(); +} + +void StartIOThread() { io_thread = std::thread(IOThread); } diff --git a/works/life/computer-network-experiment/IO.h b/works/life/computer-network-experiment/IO.h new file mode 100644 index 0000000..b0cf489 --- /dev/null +++ b/works/life/computer-network-experiment/IO.h @@ -0,0 +1,66 @@ +#pragma once +#include "Common.h" +#include "StringUtil.hpp" + +#include +#include +#include +#include + +#include +#include + +enum class OutputType { Normal, Error }; +enum class OutputColor { Normal, Green, Red, Yellow }; + +struct Output { + Output() = default; + Output(String message, OutputType type = OutputType::Normal) + : message(std::move(message)), type(type), + color(type == OutputType::Error ? OutputColor::Red + : OutputColor::Normal) {} + + Output(String message, OutputColor color) + : message(std::move(message)), type(OutputType::Normal), color(color) {} + + Output(String message, OutputType type, OutputColor color) + : message(std::move(message)), type(type), color(color) {} + + CRU_DEFAULT_COPY(Output) + CRU_DEFAULT_MOVE(Output) + ~Output() = default; + + String message; + OutputType type; + OutputColor color; +}; + +extern folly::MPMCQueue output_queue; + +inline void SendOutput(Output output) { + output_queue.blockingWrite(std::move(output)); +} + +inline void SendOutput(String output) { SendOutput(Output{std::move(output)}); } + +template void SendOutput(StringView format, Args &&...args) { + output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); +} + +template +void SendOutput(OutputType type, StringView format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), type}); +} + +template +void SendOutput(OutputColor color, StringView format, Args &&...args) { + output_queue.blockingWrite( + Output{fmt::format(format, std::forward(args)...), color}); +} + +void SignalAndWaitForOutputThreadStop(); + +void OnInputLine(StringView line); + +void StartIOThread(); diff --git a/works/life/computer-network-experiment/Output.cpp b/works/life/computer-network-experiment/Output.cpp deleted file mode 100644 index fbbd6ba..0000000 --- a/works/life/computer-network-experiment/Output.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "Output.h" - -#include - -#include -#include -#include - -std::mutex m; - -folly::MPMCQueue output_queue(100); - -folly::CancellationSource cancellation_source; - -std::thread output_thread(OutputThread); - -void PrintOutput(const Output &output) { - std::basic_ostream *stream; - - switch (output.type) { - case OutputType::Error: - stream = &error_stream; - break; - default: - stream = &output_stream; - break; - } - - switch (output.color) { - case OutputColor::Normal: - (*stream) << output.message; - break; - case OutputColor::Green: - (*stream) << CRUT("\x1b[32m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - case OutputColor::Red: - (*stream) << CRUT("\x1b[31m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - case OutputColor::Yellow: - (*stream) << CRUT("\x1b[33m") << output.message << CRUT("\x1b[39m") - << std::flush; - break; - } -} - -void OutputThread() { - while (true) { - std::lock_guard guard(m); - - if (cancellation_source.getToken().isCancellationRequested()) { - while (true) { - Output output; - if (output_queue.readIfNotEmpty(output)) { - PrintOutput(output); - } else { - return; - } - } - } - - Output output; - if (output_queue.readIfNotEmpty(output)) - PrintOutput(output); - } -} - -void SignalAndWaitForOutputThreadStop() { - cancellation_source.requestCancellation(); - output_thread.join(); -} - -std::lock_guard BlockOutputThread() { - return std::lock_guard(m); -} diff --git a/works/life/computer-network-experiment/Output.h b/works/life/computer-network-experiment/Output.h deleted file mode 100644 index 689c3d3..0000000 --- a/works/life/computer-network-experiment/Output.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include "Common.h" -#include "StringUtil.hpp" - -#include -#include -#include -#include - -#include -#include -#include - -enum class OutputType { Normal, Error }; -enum class OutputColor { Normal, Green, Red, Yellow }; - -struct Output { - Output() = default; - Output(String message, OutputType type = OutputType::Normal) - : message(std::move(message)), type(type), - color(type == OutputType::Error ? OutputColor::Red - : OutputColor::Normal) {} - - Output(String message, OutputColor color) - : message(std::move(message)), type(OutputType::Normal), color(color) {} - - Output(String message, OutputType type, OutputColor color) - : message(std::move(message)), type(type), color(color) {} - - CRU_DEFAULT_COPY(Output) - CRU_DEFAULT_MOVE(Output) - ~Output() = default; - - String message; - OutputType type; - OutputColor color; -}; - -extern folly::MPMCQueue output_queue; - -inline void SendOutput(Output output) { - output_queue.blockingWrite(std::move(output)); -} - -inline void SendOutput(String output) { SendOutput(Output{std::move(output)}); } - -template void SendOutput(StringView format, Args &&...args) { - output_queue.blockingWrite(fmt::format(format, std::forward(args)...)); -} - -template -void SendOutput(OutputType type, StringView format, Args &&...args) { - output_queue.blockingWrite( - Output{fmt::format(format, std::forward(args)...), type}); -} - -template -void SendOutput(OutputColor color, StringView format, Args &&...args) { - output_queue.blockingWrite( - Output{fmt::format(format, std::forward(args)...), color}); -} - -void OutputThread(); - -void SignalAndWaitForOutputThreadStop(); - -std::lock_guard BlockOutputThread(); diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp index c25a26b..a8ce8cf 100644 --- a/works/life/computer-network-experiment/client.cpp +++ b/works/life/computer-network-experiment/client.cpp @@ -3,7 +3,7 @@ */ #include "Common.h" -#include "Output.h" +#include "IO.h" #ifdef WIN32 #include diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp index 3c87ea0..7008c7b 100644 --- a/works/life/computer-network-experiment/server.cpp +++ b/works/life/computer-network-experiment/server.cpp @@ -3,8 +3,7 @@ */ #include "Common.h" -#include "Output.h" -#include "fmt/core.h" +#include "IO.h" #include #include @@ -25,7 +24,15 @@ const auto bind_address = "127.0.0.1"; // control bind address const u_short port = 1234; // control bind port +void PrintHelp() { + SendOutput(CRUT( + "Input and run one of following command:\n\t> NOTHING -> Continue and " + "print new messages.\n\t> list -> List all connected client.\n\t> send " + "[i] [message] -> Send messages to client with number i.\n")); +} + struct Connection { + int id; std::thread thread; int socket; sockaddr_in address; @@ -35,6 +42,8 @@ struct Connection { folly::CancellationSource cancellation_source; }; +std::vector connections; + void ResponseThreadProc(Connection *connection) { auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); auto port = htons(connection->address.sin_port); @@ -77,9 +86,11 @@ void ResponseThreadProc(Connection *connection) { CloseSocket(connection->socket); } -int Main() { - std::vector connections; +void OnInputLine(StringView line) { StringStream ss{String(line)}; + ss. + } +int Main() { int server_socket; if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { @@ -105,6 +116,10 @@ int Main() { SendOutput(OutputColor::Green, CRUT("Now start to accept incoming connection.\n")); + StartIOThread(); + + int current_id = 1; + while (true) { sockaddr_in client_address; int client_socket; @@ -121,6 +136,7 @@ int Main() { } Connection connection; + connection.id = current_id++; connection.socket = client_socket; connection.address = client_address; connections.push_back(std::move(connection)); -- cgit v1.2.3 From a84887356487a4fcbd57a90f3ce17914ea8bdd0a Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 8 Jun 2021 20:22:21 +0800 Subject: import(life): ... --- .../computer-network-experiment/CMakeLists.txt | 2 +- .../computer-network-experiment/ReadWriteLock.cpp | 59 ++++++++++++++++++++++ .../computer-network-experiment/ReadWriteLock.h | 40 +++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 works/life/computer-network-experiment/ReadWriteLock.cpp create mode 100644 works/life/computer-network-experiment/ReadWriteLock.h (limited to 'works/life/computer-network-experiment/CMakeLists.txt') diff --git a/works/life/computer-network-experiment/CMakeLists.txt b/works/life/computer-network-experiment/CMakeLists.txt index 09c1ea4..2ffd481 100644 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ b/works/life/computer-network-experiment/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_CXX_STANDARD 17) find_package(fmt CONFIG REQUIRED) find_package(Microsoft.GSL CONFIG REQUIRED) -add_library(base STATIC Common.cpp StringUtil.cpp IO.cpp) +add_library(base STATIC Common.cpp StringUtil.cpp IO.cpp ReadWriteLock.cpp) target_link_libraries(base PUBLIC Microsoft.GSL::GSL fmt::fmt Folly::folly) if(WIN32) target_link_libraries(base PUBLIC Ws2_32) diff --git a/works/life/computer-network-experiment/ReadWriteLock.cpp b/works/life/computer-network-experiment/ReadWriteLock.cpp new file mode 100644 index 0000000..d9b6e3e --- /dev/null +++ b/works/life/computer-network-experiment/ReadWriteLock.cpp @@ -0,0 +1,59 @@ +#include "ReadWriteLock.h" + +#include +#include + +namespace cru { +ReadWriteLock::ReadWriteLock() { +#ifdef WIN32 + lock_ = std::make_unique(); + InitializeSRWLock(lock_.get()); +#else +#endif +} + +ReadWriteLock::ReadWriteLock(ReadWriteLock &&other) + : lock_(std::move(other.lock_)) {} + +ReadWriteLock &ReadWriteLock::operator=(ReadWriteLock &&other) { + if (this != &other) { + Destroy(); + lock_ = std::move(other.lock_); + } + return *this; +} + +ReadWriteLock::~ReadWriteLock() { Destroy(); } + +void ReadWriteLock::ReadLock() { + assert(lock_); + AcquireSRWLockShared(lock_.get()); +} + +void ReadWriteLock::WriteLock() { + assert(lock_); + AcquireSRWLockExclusive(lock_.get()); +} + +bool ReadWriteLock::ReadTryLock() { + assert(lock_); + return TryAcquireSRWLockShared(lock_.get()) != 0; +} + +bool ReadWriteLock::WriteTryLock() { + assert(lock_); + return TryAcquireSRWLockExclusive(lock_.get()) != 0; +} + +void ReadWriteLock::ReadUnlock() { + assert(lock_); + ReleaseSRWLockShared(lock_.get()); +} + +void ReadWriteLock::WriteUnlock() { + assert(lock_); + ReleaseSRWLockExclusive(lock_.get()); +} + +void ReadWriteLock::Destroy() {} +} // namespace cru \ No newline at end of file diff --git a/works/life/computer-network-experiment/ReadWriteLock.h b/works/life/computer-network-experiment/ReadWriteLock.h new file mode 100644 index 0000000..c9889da --- /dev/null +++ b/works/life/computer-network-experiment/ReadWriteLock.h @@ -0,0 +1,40 @@ +#pragma once +#include "Common.h" + +#include + +#ifdef WIN32 +#include +#else +#endif + +namespace cru { +class ReadWriteLock { +public: + ReadWriteLock(); + + ReadWriteLock(ReadWriteLock &&other); + ReadWriteLock &operator=(ReadWriteLock &&other); + + ~ReadWriteLock(); + +public: + void ReadLock(); + void WriteLock(); + + bool ReadTryLock(); + bool WriteTryLock(); + + void ReadUnlock(); + void WriteUnlock(); + +private: + void Destroy(); + +private: +#ifdef WIN32 + std::unique_ptr lock_; +#else +#endif +}; +} // namespace cru -- cgit v1.2.3