From dc1f0c4c0096013799416664894c5194dc7e1f52 Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Fri, 28 Feb 2025 23:13:39 +0800 Subject: chore(store): move everything to store. --- works/life/computer-network-experiment/.gitignore | 5 - .../.vscode/launch.json | 16 - works/life/computer-network-experiment/Base.hpp | 59 ---- .../computer-network-experiment/CMakeLists.txt | 23 -- works/life/computer-network-experiment/Common.cpp | 142 --------- works/life/computer-network-experiment/Common.h | 54 ---- works/life/computer-network-experiment/IO.cpp | 81 ----- works/life/computer-network-experiment/IO.h | 68 ---- .../life/computer-network-experiment/PreConfig.hpp | 6 - .../computer-network-experiment/ReadWriteLock.cpp | 97 ------ .../computer-network-experiment/ReadWriteLock.h | 45 --- .../computer-network-experiment/StringUtil.cpp | 355 --------------------- .../computer-network-experiment/StringUtil.hpp | 158 --------- works/life/computer-network-experiment/TODO.md | 6 - works/life/computer-network-experiment/client.cpp | 103 ------ works/life/computer-network-experiment/server.cpp | 261 --------------- 16 files changed, 1479 deletions(-) delete mode 100644 works/life/computer-network-experiment/.gitignore delete mode 100644 works/life/computer-network-experiment/.vscode/launch.json delete mode 100644 works/life/computer-network-experiment/Base.hpp delete mode 100644 works/life/computer-network-experiment/CMakeLists.txt delete mode 100644 works/life/computer-network-experiment/Common.cpp delete mode 100644 works/life/computer-network-experiment/Common.h delete mode 100644 works/life/computer-network-experiment/IO.cpp delete mode 100644 works/life/computer-network-experiment/IO.h delete mode 100644 works/life/computer-network-experiment/PreConfig.hpp delete mode 100644 works/life/computer-network-experiment/ReadWriteLock.cpp delete mode 100644 works/life/computer-network-experiment/ReadWriteLock.h delete mode 100644 works/life/computer-network-experiment/StringUtil.cpp delete mode 100644 works/life/computer-network-experiment/StringUtil.hpp delete mode 100644 works/life/computer-network-experiment/TODO.md delete mode 100644 works/life/computer-network-experiment/client.cpp delete mode 100644 works/life/computer-network-experiment/server.cpp (limited to 'works/life/computer-network-experiment') diff --git a/works/life/computer-network-experiment/.gitignore b/works/life/computer-network-experiment/.gitignore deleted file mode 100644 index 502724c..0000000 --- a/works/life/computer-network-experiment/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.exe -*.pdb -.cache -build -compile_commands.json \ No newline at end of file diff --git a/works/life/computer-network-experiment/.vscode/launch.json b/works/life/computer-network-experiment/.vscode/launch.json deleted file mode 100644 index 882a540..0000000 --- a/works/life/computer-network-experiment/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch server.", - "type": "gdb", - "request": "launch", - "target": "${workspaceRoot}/build/server", - "cwd": "${workspaceRoot}", - "valuesFormatting": "parseText" - } - ] -} \ No newline at end of file diff --git a/works/life/computer-network-experiment/Base.hpp b/works/life/computer-network-experiment/Base.hpp deleted file mode 100644 index b1ad55c..0000000 --- a/works/life/computer-network-experiment/Base.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#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 deleted file mode 100644 index 2ffd481..0000000 --- a/works/life/computer-network-experiment/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -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) - -find_package(fmt CONFIG REQUIRED) -find_package(Microsoft.GSL CONFIG REQUIRED) -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) -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) diff --git a/works/life/computer-network-experiment/Common.cpp b/works/life/computer-network-experiment/Common.cpp deleted file mode 100644 index 1df4d56..0000000 --- a/works/life/computer-network-experiment/Common.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "Common.h" - -#include "IO.h" -#include - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#include -#endif - -#include - -[[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 - } - - BeforeExit(); - - std::exit(1); -} - -#ifdef WIN32 -namespace { -void InitWSA() { - WSADATA wsa_data; - - if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { // initialize wsa - PrintErrorMessageAndExit(CRUT("Failed to initialize wsa.")); - } -} -} // namespace - -#endif - -int CloseSocket(int socket) { -#ifdef WIN32 - return closesocket(socket); -#else - return close(socket); -#endif -} - -void BeforeExit() { -#ifdef WIN32 - WSACleanup(); -#endif - - SignalAndWaitForOutputThreadStop(); -} - -bool SafeSend(int socket, std::string_view buffer) { - const int total_byte_count = buffer.size(); - int byte_count_sent = 0; - int retry_count = 0; - - while (true) { - // Now we have sent all data. - if (byte_count_sent == total_byte_count) - return true; - - auto byte_actually_sent = send(socket, buffer.data() + byte_count_sent, - buffer.size() - byte_count_sent, 0); - - // send failed - if (byte_actually_sent == -1) { - return false; - } - - byte_count_sent += byte_actually_sent; - } -} - -bool SafeReadUntil(int socket, char c, std::string &data, std::string &rest) { - data = rest; - - const int buffer_size = 100; - char buffer[buffer_size]; - - while (true) { - int received_number = recv(socket, buffer, buffer_size, 0); - - if (received_number == -1) { - return false; - } - - bool end = false; - - for (int i = 0; i < received_number; i++) { - if (buffer[i] == c) { - data.append(buffer, i); - rest = std::string(buffer + i + 1, received_number - i - 1); - end = true; - break; - } - } - - if (end) - return true; - - data.append(buffer, received_number); - } -} - -int main() { -#ifdef WIN32 - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD mode; - GetConsoleMode(h, &mode); - mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(h, mode); - - InitWSA(); -#endif - - int c = Main(); - - BeforeExit(); - return c; -} diff --git a/works/life/computer-network-experiment/Common.h b/works/life/computer-network-experiment/Common.h deleted file mode 100644 index 1e6c277..0000000 --- a/works/life/computer-network-experiment/Common.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include "StringUtil.hpp" - -#include -#include -#include -#include - -#ifdef WIN32 -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; -#define CRUT(string_literal) L##string_literal - -inline String ConvertCharString(std::string_view s) { - return cru::ToUtf16WString(s); -} - -inline std::string ConvertCharStringBack(StringView s) { - return cru::ToUtf8(s); -} -#else -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; -#define CRUT(string_literal) string_literal - -inline String ConvertCharString(std::string_view s) { return String(s); } -inline std::string ConvertCharStringBack(StringView s) { - return std::string(s); -} -#endif - -int Main(); - -[[noreturn]] void PrintErrorMessageAndExit(StringView message, - bool print_last_error = true); - -int CloseSocket(int socket); - -void BeforeExit(); - -// Return false for error. -bool SafeSend(int socket, std::string_view buffer); -// Return false for error. -bool SafeReadUntil(int socket, char c, std::string &data, std::string &rest); diff --git a/works/life/computer-network-experiment/IO.cpp b/works/life/computer-network-experiment/IO.cpp deleted file mode 100644 index 5d3fe12..0000000 --- a/works/life/computer-network-experiment/IO.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "IO.h" - -#include - -#include -#include -#include -#include - -folly::MPMCQueue output_queue(100); - -namespace { -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 deleted file mode 100644 index 1658b78..0000000 --- a/works/life/computer-network-experiment/IO.h +++ /dev/null @@ -1,68 +0,0 @@ -#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(); - -String ReadInputLine(); \ No newline at end of file diff --git a/works/life/computer-network-experiment/PreConfig.hpp b/works/life/computer-network-experiment/PreConfig.hpp deleted file mode 100644 index d78292c..0000000 --- a/works/life/computer-network-experiment/PreConfig.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#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/ReadWriteLock.cpp b/works/life/computer-network-experiment/ReadWriteLock.cpp deleted file mode 100644 index 46d2857..0000000 --- a/works/life/computer-network-experiment/ReadWriteLock.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "ReadWriteLock.h" - -#include -#include - -namespace cru { -ReadWriteLock::ReadWriteLock() { -#ifdef WIN32 - lock_ = std::make_unique(); - InitializeSRWLock(lock_.get()); -#else - lock_.reset(new pthread_rwlock_t(PTHREAD_RWLOCK_INITIALIZER)); - pthread_rwlock_init(lock_.get(), nullptr); -#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_); -#ifdef WIN32 - AcquireSRWLockShared(lock_.get()); -#else - pthread_rwlock_rdlock(lock_.get()); -#endif -} - -void ReadWriteLock::WriteLock() { - assert(lock_); -#ifdef WIN32 - AcquireSRWLockExclusive(lock_.get()); -#else - pthread_rwlock_wrlock(lock_.get()); -#endif -} - -bool ReadWriteLock::ReadTryLock() { - assert(lock_); -#ifdef WIN32 - return TryAcquireSRWLockShared(lock_.get()) != 0; -#else - return pthread_rwlock_tryrdlock(lock_.get()) == 0; -#endif -} - -bool ReadWriteLock::WriteTryLock() { - assert(lock_); -#ifdef WIN32 - return TryAcquireSRWLockExclusive(lock_.get()) != 0; -#else - return pthread_rwlock_trywrlock(lock_.get()) == 0; -#endif -} - -void ReadWriteLock::ReadUnlock() { - assert(lock_); -#ifdef WIN32 - ReleaseSRWLockShared(lock_.get()); -#else - pthread_rwlock_unlock(lock_.get()); -#endif -} - -void ReadWriteLock::WriteUnlock() { - assert(lock_); -#ifdef WIN32 - ReleaseSRWLockExclusive(lock_.get()); -#else - pthread_rwlock_unlock(lock_.get()); -#endif -} - -void ReadWriteLock::Destroy() { -#ifndef WIN32 - if (lock_ != nullptr) - pthread_rwlock_destroy(lock_.get()); -#endif -} - -void swap(ReadWriteLock &left, ReadWriteLock &right) { - auto temp = std::move(left.lock_); - left.lock_ = std::move(right.lock_); - right.lock_ = std::move(temp); -} - -} // 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 deleted file mode 100644 index ee40ac0..0000000 --- a/works/life/computer-network-experiment/ReadWriteLock.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include "Common.h" - -#include - -#ifdef WIN32 -#include -#else -#include -#endif - -namespace cru { -class ReadWriteLock { - friend void swap(ReadWriteLock &left, ReadWriteLock &right); -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 - std::unique_ptr lock_; -#endif -}; - -void swap(ReadWriteLock &left, ReadWriteLock &right); -} // namespace cru diff --git a/works/life/computer-network-experiment/StringUtil.cpp b/works/life/computer-network-experiment/StringUtil.cpp deleted file mode 100644 index 6bf906d..0000000 --- a/works/life/computer-network-experiment/StringUtil.cpp +++ /dev/null @@ -1,355 +0,0 @@ -#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::string ToUtf8(std::wstring_view s) { - std::u16string_view string{reinterpret_cast(s.data()), - s.size()}; - std::string result; - for (CodePoint cp : Utf16CodePointIterator{string}) { - Utf8EncodeCodePointAppend(cp, result); - } - return result; -} -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 deleted file mode 100644 index b0ca675..0000000 --- a/works/life/computer-network-experiment/StringUtil.hpp +++ /dev/null @@ -1,158 +0,0 @@ -#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::string ToUtf8(std::wstring_view s); -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/TODO.md b/works/life/computer-network-experiment/TODO.md deleted file mode 100644 index 248a2d7..0000000 --- a/works/life/computer-network-experiment/TODO.md +++ /dev/null @@ -1,6 +0,0 @@ -1. Apply read-write lock to connections. ✅ -2. Remove dead connection from connection list. ✅ -3. Handle SIGINT gracefully. -4. Add close method to protocol. -5. Add help and exit command. -6. Use multiprocess to show output. diff --git a/works/life/computer-network-experiment/client.cpp b/works/life/computer-network-experiment/client.cpp deleted file mode 100644 index 73ae52f..0000000 --- a/works/life/computer-network-experiment/client.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/** Created by crupest. - * This is the client program. - */ - -#include "Common.h" -#include "IO.h" - -#include -#include - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#endif - -const auto connect_address = "127.0.0.1"; // control connect address -const u_short port = 1234; // control connect port - -namespace { -folly::ProducerConsumerQueue send_queue(100); -folly::CancellationSource cancellation_source; -} // namespace - -void PrintHelp() { - SendOutput(CRUT("Input anything to send to server. Or just enter to receive " - "lastest messages from server.\n")); -} - -void OnInputLine(StringView line) { - if (line.empty()) { - return; - } else { - send_queue.write(ConvertCharStringBack(line) + '\n'); - } -} - -int Main() { - int client_socket; - - if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - PrintErrorMessageAndExit(CRUT("Failed to create socket!\n")); - } - - sockaddr_in server_address; - - server_address.sin_family = AF_INET; - server_address.sin_port = htons(port); - server_address.sin_addr.s_addr = inet_addr(connect_address); - memset(&(server_address.sin_zero), 0, sizeof(server_address.sin_zero)); - - if (connect(client_socket, (sockaddr *)&server_address, sizeof(sockaddr)) == - -1) { - PrintErrorMessageAndExit(CRUT("Failed to connect!")); - } - - output_stream << CRUT("Please input your name:\n> "); - String name = ReadInputLine(); - - PrintHelp(); - - StartIOThread(); - - name.push_back(CRUT('\n')); - auto name_data = ConvertCharStringBack(name); - SafeSend(client_socket, name_data); - - std::thread receive_thread([client_socket] { - std::string rest; - while (true) { - if (cancellation_source.isCancellationRequested()) { - break; - } - - std::string data; - if (!SafeReadUntil(client_socket, '\n', data, rest)) { - PrintErrorMessageAndExit(CRUT("Failed to receive message.\n")); - } - - SendOutput(CRUT("Recived a message:\n{}\n"), ConvertCharString(data)); - } - }); - receive_thread.detach(); - - while (true) { - if (cancellation_source.isCancellationRequested()) { - break; - } - - std::string s; - if (send_queue.read(s)) { - if (!SafeSend(client_socket, s)) { - PrintErrorMessageAndExit(CRUT("Failed to send message to server.")); - } - } - } - - CloseSocket(client_socket); - return 0; -} diff --git a/works/life/computer-network-experiment/server.cpp b/works/life/computer-network-experiment/server.cpp deleted file mode 100644 index 065687c..0000000 --- a/works/life/computer-network-experiment/server.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/** Created by crupest. - * This is the server program. - */ - -#include "Common.h" -#include "IO.h" -#include "ReadWriteLock.h" - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#endif - -const auto bind_address = "127.0.0.1"; // control bind address -const u_short port = 1234; // control bind port - -namespace { -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")); -} -} // namespace - -struct Connection { - int id; - std::thread thread; - std::thread receive_thread; - int socket; - sockaddr_in address; - String address_string; - String name; - folly::ProducerConsumerQueue send_queue{100}; - folly::CancellationSource cancellation_source; -}; - -namespace { -cru::ReadWriteLock connections_lock; -std::vector> connections; - -void RemoveConnection(int id) { - connections_lock.WriteLock(); - connections.erase( - std::remove_if(connections.begin(), connections.end(), - [id](const std::unique_ptr &connection) { - return connection->id == id; - }), - connections.end()); - - connections_lock.WriteUnlock(); -} - -void PrintConnections() { - connections_lock.ReadLock(); - if (connections.empty()) { - SendOutput(CRUT("Currently there is no connection.\n")); - } - - String s; - for (const auto &connection : connections) { - s += fmt::format(CRUT("{}: {}({})\n"), connection->id, connection->name, - connection->address_string); - } - SendOutput(s); - connections_lock.ReadUnlock(); -} -} // namespace - -void ResponseThreadProc(Connection *connection) { - auto host = ConvertCharString(inet_ntoa(connection->address.sin_addr)); - auto port = htons(connection->address.sin_port); - connection->address_string = fmt::format(CRUT("{}:{}"), host, port); - - std::string rest; - std::string name_data; - if (!SafeReadUntil(connection->socket, '\n', name_data, rest)) { - SendOutput(OutputType::Error, CRUT("Failed to read name of {}.\n"), - connection->address_string); - CloseSocket(connection->socket); - return; - } - - connection->name = ConvertCharString(name_data); - SendOutput(OutputColor::Green, CRUT("Connected to {}, whose name is {}.\n"), - connection->address_string, connection->name); - - connection->receive_thread = std::thread( - [](Connection *connection) { - std::string rest; - while (true) { - if (connection->cancellation_source.isCancellationRequested()) { - break; - } - - std::string data; - - if (!SafeReadUntil(connection->socket, '\n', data, rest)) { - SendOutput(OutputType::Error, - CRUT("Failed read data from socket of {}({}).\n"), - connection->name, connection->address_string); - connection->cancellation_source.requestCancellation(); - return; - } - - SendOutput(CRUT("{}({}) send a message:\n{}\n"), connection->name, - connection->address_string, ConvertCharString(data)); - } - }, - connection); - connection->receive_thread.detach(); - - while (true) { - if (connection->cancellation_source.isCancellationRequested()) { - break; - } - - std::string s; - if (connection->send_queue.read(s)) { - if (!SafeSend(connection->socket, s)) { - SendOutput(OutputType::Error, CRUT("Failed send data to {}({}).\n"), - connection->name, connection->address_string); - connection->cancellation_source.requestCancellation(); - break; - } - } - } - - CloseSocket(connection->socket); - - RemoveConnection(connection->id); -} - -void OnInputLine(StringView line) { - StringStream ss{String(line)}; - - ss >> std::ws; - if (ss.eof()) - return; - - String command; - ss >> command; - - if (command == CRUT("list")) { - if (!ss.eof()) { - SendOutput(OutputType::Error, - CRUT("List command can't have arguments!\n")); - PrintHelp(); - } else { - PrintConnections(); - } - return; - } else if (command == CRUT("send")) { - int id; - ss >> id; - if (!ss) { - SendOutput(OutputType::Error, CRUT("Send format error!\n")); - PrintHelp(); - return; - } - - String message; - getline(ss, message); - - if (message.empty()) { - SendOutput(OutputType::Error, CRUT("Send message can't be empty.!\n")); - PrintHelp(); - return; - } - - auto i = std::find_if( - connections.begin(), connections.end(), - [id](const std::unique_ptr &c) { return c->id == id; }); - - if (i == connections.end()) { - SendOutput(OutputType::Error, CRUT("No connection with such id.\n")); - return; - } - - (*i)->send_queue.write(ConvertCharStringBack(message) + "\n"); - return; - } else { - SendOutput(OutputType::Error, CRUT("Unkown command!\n")); - PrintHelp(); - return; - } -} - -int Main() { - int server_socket; - - if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - PrintErrorMessageAndExit(CRUT("Failed to create socket.")); - } - - sockaddr_in server_address; - - server_address.sin_family = AF_INET; - server_address.sin_port = htons(port); - server_address.sin_addr.s_addr = inet_addr(bind_address); - memset(&(server_address.sin_zero), 0, sizeof(server_address.sin_zero)); - - if (bind(server_socket, reinterpret_cast(&server_address), - sizeof(sockaddr_in)) == -1) { - PrintErrorMessageAndExit(CRUT("Failed to bind.")); - } - - if (listen(server_socket, SOMAXCONN) == -1) { - PrintErrorMessageAndExit(CRUT("Failed to listen.")); - } - - SendOutput(OutputColor::Green, - CRUT("Now start to accept incoming connection.\n")); - - PrintHelp(); - - StartIOThread(); - - int current_id = 1; - - while (true) { - sockaddr_in client_address; - int client_socket; - unsigned sin_size = sizeof(sockaddr_in); - client_socket = - accept(server_socket, reinterpret_cast(&client_address), -#ifdef WIN32 - reinterpret_cast -#endif - (&sin_size)); - - if (client_socket == -1) { - PrintErrorMessageAndExit(CRUT("Failed to accecpt.")); - } - - connections_lock.WriteLock(); - connections.push_back(std::make_unique()); - const std::unique_ptr &connection = connections.back(); - - connection->id = current_id++; - connection->socket = client_socket; - connection->address = client_address; - connection->thread = - std::thread(ResponseThreadProc, connections.back().get()); - connection->thread.detach(); - connections_lock.WriteUnlock(); - } -} -- cgit v1.2.3