diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt index f7f1baeafe34261a38033dd3d2db828df334ce25..c4edd2505b39320d39b25b7653667f7992567ccc 100644 --- a/src/language/modules/CMakeLists.txt +++ b/src/language/modules/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(PugsLanguageModules MeshModule.cpp ModuleRepository.cpp SchemeModule.cpp + SocketModule.cpp UnaryOperatorRegisterForVh.cpp UtilsModule.cpp WriterModule.cpp diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp index 71a1368b9dc374b19562bae27cc14a5d2b54c233..85810cbfc3f12bba920ae2ee99002be73f0db39d 100644 --- a/src/language/modules/ModuleRepository.cpp +++ b/src/language/modules/ModuleRepository.cpp @@ -6,6 +6,7 @@ #include <language/modules/MathModule.hpp> #include <language/modules/MeshModule.hpp> #include <language/modules/SchemeModule.hpp> +#include <language/modules/SocketModule.hpp> #include <language/modules/UtilsModule.hpp> #include <language/modules/WriterModule.hpp> #include <language/utils/BasicAffectationRegistrerFor.hpp> @@ -56,6 +57,7 @@ ModuleRepository::ModuleRepository() this->_subscribe(std::make_unique<MathModule>()); this->_subscribe(std::make_unique<MeshModule>()); this->_subscribe(std::make_unique<SchemeModule>()); + this->_subscribe(std::make_unique<SocketModule>()); this->_subscribe(std::make_unique<UtilsModule>()); this->_subscribe(std::make_unique<WriterModule>()); } diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp index fcebda3dedc014d935a7e73a96785762f4cb00e1..f80201c7e424728db5663045e5b663c5e306e4d4 100644 --- a/src/language/modules/SchemeModule.cpp +++ b/src/language/modules/SchemeModule.cpp @@ -26,6 +26,7 @@ #include <scheme/DiscreteFunctionUtils.hpp> #include <scheme/DiscreteFunctionVectorIntegrator.hpp> #include <scheme/DiscreteFunctionVectorInterpoler.hpp> +#include <scheme/ExternalBoundaryConditionDescriptor.hpp> #include <scheme/FixedBoundaryConditionDescriptor.hpp> #include <scheme/FourierBoundaryConditionDescriptor.hpp> #include <scheme/FreeBoundaryConditionDescriptor.hpp> @@ -36,6 +37,7 @@ #include <scheme/NeumannBoundaryConditionDescriptor.hpp> #include <scheme/NumberedBoundaryDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Socket.hpp> #include <memory> @@ -273,6 +275,20 @@ SchemeModule::SchemeModule() )); + this->_addBuiltinFunction("external_fsi_velocity", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr< + const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>, + const std::shared_ptr<const Socket>&)>>( + + [](std::shared_ptr<const IBoundaryDescriptor> boundary, + const std::shared_ptr<const Socket>& socket) + -> std::shared_ptr<const IBoundaryConditionDescriptor> { + return std::make_shared<ExternalBoundaryConditionDescriptor>("external_fsi_velocity", + boundary, socket); + } + + )); + this->_addBuiltinFunction("glace_solver", std::make_shared<BuiltinFunctionEmbedder<std::tuple< std::shared_ptr<const IMesh>, std::shared_ptr<const IDiscreteFunction>, diff --git a/src/language/modules/SocketModule.cpp b/src/language/modules/SocketModule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0540af2b60553c33f61ccaeeed049d9696f6ba5e --- /dev/null +++ b/src/language/modules/SocketModule.cpp @@ -0,0 +1,291 @@ +#include <language/modules/SocketModule.hpp> + +#include <language/utils/BinaryOperatorProcessorBuilder.hpp> +#include <language/utils/BuiltinFunctionEmbedder.hpp> +#include <language/utils/OStream.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <utils/Socket.hpp> + +SocketModule::SocketModule() +{ + this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const Socket>>); + + this->_addBuiltinFunction("createSocketServer", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const uint64_t&)>>( + + [](const uint64_t& port_number) -> std::shared_ptr<const Socket> { + return std::make_shared<const Socket>(createServerSocket(port_number)); + } + + )); + + this->_addBuiltinFunction("acceptSocketClient", + std::make_shared< + BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(std::shared_ptr<const Socket>)>>( + + [](std::shared_ptr<const Socket> server_socket) -> std::shared_ptr<const Socket> { + return std::make_shared<const Socket>(acceptClientSocket(*server_socket)); + } + + )); + + this->_addBuiltinFunction("connectSocketServer", + std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const std::string&, + const uint64_t&)>>( + + [](const std::string& hostname, + const uint64_t& port_number) -> std::shared_ptr<const Socket> { + return std::make_shared<const Socket>(connectServerSocket(hostname, port_number)); + } + + )); + + this->_addBuiltinFunction("write", + std::make_shared< + BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const bool&)>>( + + [](const std::shared_ptr<const Socket>& socket, const bool& value) -> void { + write(*socket, value); + } + + )); + + this->_addBuiltinFunction("write", + std::make_shared< + BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const uint64_t&)>>( + + [](const std::shared_ptr<const Socket>& socket, const uint64_t& value) -> void { + write(*socket, value); + } + + )); + + this->_addBuiltinFunction("write", + std::make_shared< + BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const int64_t&)>>( + + [](const std::shared_ptr<const Socket>& socket, const int64_t& value) -> void { + write(*socket, value); + } + + )); + + this->_addBuiltinFunction("write", + std::make_shared< + BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const double&)>>( + + [](const std::shared_ptr<const Socket>& socket, const double& value) -> void { + write(*socket, value); + } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyVector<1>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyVector<1>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyVector<2>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyVector<2>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyVector<3>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyVector<3>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyMatrix<1>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyMatrix<1>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyMatrix<2>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyMatrix<2>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, + const TinyMatrix<3>&)>>( + + [](const std::shared_ptr<const Socket>& socket, + const TinyMatrix<3>& value) -> void { write(*socket, value); } + + )); + + this->_addBuiltinFunction("write", + std::make_shared< + BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const std::string&)>>( + + [](const std::shared_ptr<const Socket>& socket, const std::string& value) -> void { + write(*socket, value.size()); + write(*socket, value); + } + + )); + + this->_addBuiltinFunction("read_B", + std::make_shared<BuiltinFunctionEmbedder<bool(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> bool { + bool value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_N", + std::make_shared<BuiltinFunctionEmbedder<uint64_t(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> uint64_t { + uint64_t value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_Z", + std::make_shared<BuiltinFunctionEmbedder<int64_t(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> int64_t { + int64_t value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R", + std::make_shared<BuiltinFunctionEmbedder<double(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> double { + double value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R1", std::make_shared< + BuiltinFunctionEmbedder<TinyVector<1>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyVector<1> { + TinyVector<1> value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R2", std::make_shared< + BuiltinFunctionEmbedder<TinyVector<2>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyVector<2> { + TinyVector<2> value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R3", std::make_shared< + BuiltinFunctionEmbedder<TinyVector<3>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyVector<3> { + TinyVector<3> value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R1x1", + std::make_shared< + BuiltinFunctionEmbedder<TinyMatrix<1>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<1> { + TinyMatrix<1> value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R2x2", + std::make_shared< + BuiltinFunctionEmbedder<TinyMatrix<2>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<2> { + TinyMatrix<2> value; + read(*socket, value); + + return value; + } + + )); + + this->_addBuiltinFunction("read_R3x3", + std::make_shared< + BuiltinFunctionEmbedder<TinyMatrix<3>(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<3> { + TinyMatrix<3> value; + read(*socket, value); + + return value; + } + + )); + + this + ->_addBuiltinFunction("read_string", + std::make_shared<BuiltinFunctionEmbedder<std::string(const std::shared_ptr<const Socket>&)>>( + + [](const std::shared_ptr<const Socket>& socket) -> std::string { + size_t size; + read(*socket, size); + std::string value; + if (size > 0) { + value.resize(size); + read(*socket, value); + } + return value; + } + + )); +} + +void +SocketModule::registerOperators() const +{ + OperatorRepository& repository = OperatorRepository::instance(); + + repository.addBinaryOperator<language::shift_left_op>( + std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>, + std::shared_ptr<const OStream>, std::shared_ptr<const Socket>>>()); +} diff --git a/src/language/modules/SocketModule.hpp b/src/language/modules/SocketModule.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0b32b64fafc72829e6598cfa78a95ce14f61ed9b --- /dev/null +++ b/src/language/modules/SocketModule.hpp @@ -0,0 +1,28 @@ +#ifndef SOCKET_MODULE_HPP +#define SOCKET_MODULE_HPP + +#include <language/modules/BuiltinModule.hpp> +#include <language/utils/ASTNodeDataTypeTraits.hpp> + +class Socket; + +template <> +inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const Socket>> = + ASTNodeDataType::build<ASTNodeDataType::type_id_t>("socket"); + +class SocketModule : public BuiltinModule +{ + public: + std::string_view + name() const final + { + return "socket"; + } + + void registerOperators() const final; + + SocketModule(); + ~SocketModule() = default; +}; + +#endif // SOCKET_MODULE_HPP diff --git a/src/language/utils/OStream.hpp b/src/language/utils/OStream.hpp index d9410e25e35d2a3e9d5ac147911f6464d3b71eb7..bef02277ef8e07be09cc5731b33c6f5c3363e37d 100644 --- a/src/language/utils/OStream.hpp +++ b/src/language/utils/OStream.hpp @@ -23,6 +23,17 @@ class OStream return os; } + template <typename DataT> + friend std::shared_ptr<const OStream> + operator<<(const std::shared_ptr<const OStream>& os, const std::shared_ptr<const DataT>& t) + { + Assert(os.use_count() > 0, "non allocated stream"); + if (os->m_ostream != nullptr) { + *os->m_ostream << *t; + } + return os; + } + OStream(std::ostream& os) : m_ostream(&os) {} OStream() = default; diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp index e5171badb11fa509a020025ad38e7e7bbd96281d..098b146e55efe52ffc7297b8ebb841206d9e3d43 100644 --- a/src/scheme/AcousticSolver.cpp +++ b/src/scheme/AcousticSolver.cpp @@ -8,11 +8,13 @@ #include <scheme/DirichletBoundaryConditionDescriptor.hpp> #include <scheme/DiscreteFunctionP0.hpp> #include <scheme/DiscreteFunctionUtils.hpp> +#include <scheme/ExternalBoundaryConditionDescriptor.hpp> #include <scheme/FixedBoundaryConditionDescriptor.hpp> #include <scheme/IBoundaryConditionDescriptor.hpp> #include <scheme/IDiscreteFunction.hpp> #include <scheme/IDiscreteFunctionDescriptor.hpp> #include <scheme/SymmetryBoundaryConditionDescriptor.hpp> +#include <utils/Socket.hpp> #include <variant> #include <vector> @@ -75,9 +77,13 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler class PressureBoundaryCondition; class SymmetryBoundaryCondition; class VelocityBoundaryCondition; + class ExternalFSIVelocityBoundaryCondition; - using BoundaryCondition = std:: - variant<FixedBoundaryCondition, PressureBoundaryCondition, SymmetryBoundaryCondition, VelocityBoundaryCondition>; + using BoundaryCondition = std::variant<FixedBoundaryCondition, + PressureBoundaryCondition, + SymmetryBoundaryCondition, + VelocityBoundaryCondition, + ExternalFSIVelocityBoundaryCondition>; using BoundaryConditionList = std::vector<BoundaryCondition>; @@ -254,6 +260,18 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler bc_list.emplace_back(FixedBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()))); break; } + case IBoundaryConditionDescriptor::Type::external: { + if constexpr (Dimension == 1) { + const ExternalBoundaryConditionDescriptor& extern_bc_descriptor = + dynamic_cast<const ExternalBoundaryConditionDescriptor&>(*bc_descriptor); + bc_list.emplace_back( + ExternalFSIVelocityBoundaryCondition(mesh, getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()), + extern_bc_descriptor.socket())); + } else { + throw NotImplementedError("external FSI velocity not implemented for dimension > 1"); + } + break; + } case IBoundaryConditionDescriptor::Type::dirichlet: { const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor = dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor); @@ -320,6 +338,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler void _applyPressureBC(const MeshType& mesh, NodeValue<Rd>& br); void _applySymmetryBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br); void _applyVelocityBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br); + void _applyExternalVelocityBC(const DiscreteScalarFunction& p, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br); void _applyBoundaryConditions(const MeshType& mesh, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br) @@ -380,6 +399,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler NodeValue<Rd> br = this->_computeBr(mesh, Ajr, u, p); this->_applyBoundaryConditions(mesh, Ar, br); + this->_applyExternalVelocityBC(p, Ar, br); synchronize(Ar); synchronize(br); @@ -610,6 +630,34 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyVelocityBC(NodeValue<Rdx } } +template <size_t Dimension> +void +AcousticSolverHandler::AcousticSolver<Dimension>::_applyExternalVelocityBC(const DiscreteScalarFunction& p, + NodeValue<Rdxd>& Ar, + NodeValue<Rd>& br) +{ + for (const auto& boundary_condition : m_boundary_condition_list) { + std::visit( + [&](auto&& bc) { + using T = std::decay_t<decltype(bc)>; + if constexpr (std::is_same_v<ExternalFSIVelocityBoundaryCondition, T>) { + const auto& node_list = bc.nodeList(); + const auto& value_list = bc.valueList(p); + + parallel_for( + node_list.size(), PUGS_LAMBDA(size_t i_node) { + NodeId node_id = node_list[i_node]; + const auto& value = value_list[i_node]; + + Ar[node_id] = identity; + br[node_id] = value; + }); + } + }, + boundary_condition); + } +} + template <size_t Dimension> class AcousticSolverHandler::AcousticSolver<Dimension>::FixedBoundaryCondition { @@ -685,6 +733,55 @@ class AcousticSolverHandler::AcousticSolver<1>::PressureBoundaryCondition ~PressureBoundaryCondition() = default; }; +template <size_t Dimension> +class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBoundaryCondition +{ + private: + const ItemToItemMatrix<ItemType::node, ItemType::cell> m_node_to_cell_matrix; + const MeshNodeBoundary<Dimension> m_mesh_node_boundary; + const Array<TinyVector<Dimension>> m_value_list; + const std::shared_ptr<const Socket> m_socket; + + public: + const Array<const NodeId>& + nodeList() const + { + return m_mesh_node_boundary.nodeList(); + } + + Array<const TinyVector<Dimension>> + valueList(const DiscreteScalarFunction& p) const + { + if (parallel::size() > 1) { + throw NotImplementedError("Parallelism is not supported"); + } + if (m_value_list.size() != 1) { + throw NotImplementedError("Non connected boundaries are not supported"); + } + + const CellId cell_id = m_node_to_cell_matrix[m_mesh_node_boundary.nodeList()[0]][0]; + + write(*m_socket, p[cell_id]); + read(*m_socket, m_value_list[0]); + return m_value_list; + } + + ExternalFSIVelocityBoundaryCondition(const Mesh<Connectivity<Dimension>>& mesh, + const MeshNodeBoundary<Dimension>& mesh_node_boundary, + const std::shared_ptr<const Socket>& socket) + : m_node_to_cell_matrix{mesh.connectivity().nodeToCellMatrix()}, + m_mesh_node_boundary{mesh_node_boundary}, + m_value_list(mesh_node_boundary.nodeList().size()), + m_socket{socket} + { + if constexpr (Dimension > 1) { + throw NotImplementedError("external FSI velocity bc in dimension > 1"); + } + } + + ~ExternalFSIVelocityBoundaryCondition() = default; +}; + template <size_t Dimension> class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryCondition { diff --git a/src/scheme/ExternalBoundaryConditionDescriptor.hpp b/src/scheme/ExternalBoundaryConditionDescriptor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..056592931b1ce1cf6801f7455d5e8ab3f785dc9f --- /dev/null +++ b/src/scheme/ExternalBoundaryConditionDescriptor.hpp @@ -0,0 +1,62 @@ +#ifndef EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP +#define EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP + +#include <mesh/IBoundaryDescriptor.hpp> +#include <scheme/IBoundaryConditionDescriptor.hpp> +#include <utils/Socket.hpp> + +#include <memory> + +class ExternalBoundaryConditionDescriptor : public IBoundaryConditionDescriptor +{ + private: + std::ostream& + _write(std::ostream& os) const final + { + os << "external(" << m_name << ',' << *m_boundary_descriptor << ")"; + return os; + } + + const std::string_view m_name; + + std::shared_ptr<const IBoundaryDescriptor> m_boundary_descriptor; + const std::shared_ptr<const Socket> m_socket; + + public: + std::string_view + name() const + { + return m_name; + } + + const std::shared_ptr<const Socket>& + socket() const + { + return m_socket; + } + + const IBoundaryDescriptor& + boundaryDescriptor() const final + { + return *m_boundary_descriptor; + } + + Type + type() const final + { + return Type::external; + } + + ExternalBoundaryConditionDescriptor(const std::string_view name, + std::shared_ptr<const IBoundaryDescriptor> boundary_descriptor, + const std::shared_ptr<const Socket>& socket) + : m_name{name}, m_boundary_descriptor(boundary_descriptor), m_socket{socket} + {} + + ExternalBoundaryConditionDescriptor(const ExternalBoundaryConditionDescriptor&) = delete; + ExternalBoundaryConditionDescriptor(ExternalBoundaryConditionDescriptor&&) = delete; + + ~ExternalBoundaryConditionDescriptor() = default; +}; + +#endif // EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP diff --git a/src/scheme/IBoundaryConditionDescriptor.hpp b/src/scheme/IBoundaryConditionDescriptor.hpp index c0ea161db5d5f869d8ac8517b85ae2385af1d94a..f99eff9355f6fdf6fbd65ad1898f3cba655af284 100644 --- a/src/scheme/IBoundaryConditionDescriptor.hpp +++ b/src/scheme/IBoundaryConditionDescriptor.hpp @@ -12,6 +12,7 @@ class IBoundaryConditionDescriptor { axis, dirichlet, + external, fourier, fixed, free, diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 19feb273c4a6e2ad09818f467caa9f0f79636b35..741b0d5104cef791cf045fd8cc76cd8b16db30a2 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -15,7 +15,8 @@ add_library( RandomEngine.cpp RevisionInfo.cpp SignalManager.cpp - SLEPcWrapper.cpp) + SLEPcWrapper.cpp + Socket.cpp) target_link_libraries( PugsUtils diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp index 75ef1732a496e367917b55b86e4f2075570acd86..44772529e8539814eb30b36dad7b936e4c0269a6 100644 --- a/src/utils/Messenger.hpp +++ b/src/utils/Messenger.hpp @@ -19,8 +19,6 @@ #include <utils/Exceptions.hpp> #include <utils/PugsTraits.hpp> -#include <iostream> - namespace parallel { class Messenger diff --git a/src/utils/Socket.cpp b/src/utils/Socket.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3582e359be22a257b2b52109f7a3ed57b2944a1b --- /dev/null +++ b/src/utils/Socket.cpp @@ -0,0 +1,220 @@ +#include <utils/Exceptions.hpp> +#include <utils/Socket.hpp> + +#include <utils/Messenger.hpp> + +#include <arpa/inet.h> +#include <cstring> +#include <iostream> +#include <netdb.h> +#include <netinet/in.h> +#include <stdexcept> +#include <sys/socket.h> +#include <unistd.h> + +class Socket::Internals +{ + private: + int m_socket_fd; + sockaddr_in m_address; + + const bool m_is_server_socket; + + public: + friend Socket createServerSocket(int port_number); + friend Socket acceptClientSocket(const Socket& server); + friend Socket connectServerSocket(const std::string& server_name, int port_number); + + friend std::ostream& + operator<<(std::ostream& os, const Socket::Internals& internals) + { + // This function's coverage is not performed since it's quite + // complex to create its various conditions + // + // LCOV_EXCL_START + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf, + sizeof(hbuf), sbuf, sizeof(sbuf), NI_NAMEREQD) == 0) { + os << hbuf << ':' << sbuf; + } else if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf, + sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) { + if (std ::string{hbuf} == "0.0.0.0") { + if (::gethostname(hbuf, NI_MAXHOST) == 0) { + os << hbuf << ':' << sbuf; + } else { + os << "localhost:" << sbuf; + } + } else { + os << hbuf << ':' << sbuf; + } + } else { + os << "<unknown host>"; + } + // LCOV_EXCL_STOP + return os; + } + + bool + isServerSocket() const + { + return m_is_server_socket; + } + + int + portNumber() const + { + return ::ntohs(m_address.sin_port); + } + + int + fileDescriptor() const + { + return m_socket_fd; + } + + Internals(const Internals&) = delete; + Internals(Internals&&) = delete; + + Internals(bool is_server_socket = false) : m_is_server_socket{is_server_socket} + { + if (parallel::size() > 1) { + // LCOV_EXCL_START + throw NotImplementedError("Sockets are not managed in parallel"); + // LCOV_EXCL_STOP + } + } + + ~Internals() + { + close(m_socket_fd); + } +}; + +std::ostream& +operator<<(std::ostream& os, const Socket& socket) +{ + return os << *socket.m_internals; +} + +int +Socket::portNumber() const +{ + return m_internals->portNumber(); +} + +Socket +createServerSocket(int port_number) +{ + auto p_socket_internals = std::make_shared<Socket::Internals>(true); + Socket::Internals& socket_internals = *p_socket_internals; + + socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket_internals.m_socket_fd < 0) { + // This should never happen + throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE + } + + socket_internals.m_address.sin_family = AF_INET; + socket_internals.m_address.sin_addr.s_addr = INADDR_ANY; + socket_internals.m_address.sin_port = htons(port_number); + + int on = 1; + ::setsockopt(socket_internals.m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + socklen_t length = sizeof(socket_internals.m_address); + + if (::bind(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), length) < 0) { + throw NormalError(strerror(errno)); + } + + if (::getsockname(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), &length) == + -1) { + // This should never happen + throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE + } + + ::listen(socket_internals.m_socket_fd, 1); + + return Socket{p_socket_internals}; +} + +Socket +acceptClientSocket(const Socket& server) +{ + auto p_socket_internals = std::make_shared<Socket::Internals>(); + Socket::Internals& socket_internals = *p_socket_internals; + + socklen_t address_lenght = sizeof(socket_internals.m_address); + socket_internals.m_socket_fd = ::accept(server.m_internals->m_socket_fd, + reinterpret_cast<sockaddr*>(&socket_internals.m_address), &address_lenght); + + if (socket_internals.m_socket_fd < 0) { + // This should never happen + throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE + } + + return Socket{p_socket_internals}; +} + +Socket +connectServerSocket(const std::string& server_name, int port_number) +{ + auto p_socket_internals = std::make_shared<Socket::Internals>(); + Socket::Internals& socket_internals = *p_socket_internals; + + socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket_internals.m_socket_fd < 0) { + // This should never happen + throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE + } + + hostent* server = ::gethostbyname(server_name.c_str()); + if (server == NULL) { + throw NormalError(strerror(errno)); + } + + sockaddr_in& serv_addr = socket_internals.m_address; + ::memset(reinterpret_cast<char*>(&serv_addr), 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + + ::memcpy(reinterpret_cast<char*>(&serv_addr.sin_addr.s_addr), reinterpret_cast<char*>(server->h_addr), + server->h_length); + + serv_addr.sin_port = ::htons(port_number); + + if (::connect(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr))) { + throw NormalError(strerror(errno)); + } + + return Socket{p_socket_internals}; +} + +void +Socket::_write(const char* data, const size_t lenght) const +{ + if (this->m_internals->isServerSocket()) { + throw NormalError("Server cannot write to server socket"); + } + + if (::write(this->m_internals->fileDescriptor(), data, lenght) < 0) { + // Quite complex to test + throw NormalError(strerror(errno)); // LCOV_EXCL_LINE + } +} + +void +Socket::_read(char* data, const size_t length) const +{ + if (this->m_internals->isServerSocket()) { + throw NormalError("Server cannot read from server socket"); + } + + size_t received = 0; + while (received < length) { + int n = ::read(this->m_internals->fileDescriptor(), reinterpret_cast<char*>(data) + received, length - received); + if (n <= 0) { + throw NormalError("Could not read data"); + } + received += n; + } +} diff --git a/src/utils/Socket.hpp b/src/utils/Socket.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e6d25ddd1328d559e4786e677aff8cde9af06549 --- /dev/null +++ b/src/utils/Socket.hpp @@ -0,0 +1,86 @@ +#ifndef SOCKET_HPP +#define SOCKET_HPP + +#include <memory> +#include <string> +#include <type_traits> +#include <utils/PugsTraits.hpp> + +class Socket +{ + private: + class Internals; + + std::shared_ptr<Internals> m_internals; + + Socket(std::shared_ptr<Internals> internals) : m_internals{internals} {} + + void _write(const char* data, const size_t lenght) const; + void _read(char* data, const size_t lenght) const; + + public: + int portNumber() const; + + friend std::ostream& operator<<(std::ostream& os, const Socket& s); + + friend Socket createServerSocket(int port_number); + friend Socket acceptClientSocket(const Socket& server); + friend Socket connectServerSocket(const std::string& server_name, int port_number); + + template <typename T> + friend void write(const Socket& socket, const T& value); + + template <typename T> + friend void read(const Socket& socket, T& value); + + template <template <typename T, typename... R> typename ArrayT, typename T, typename... R> + friend void write(const Socket& socket, const ArrayT<T, R...>& array); + + template <template <typename T, typename... R> typename ArrayT, typename T, typename... R> + friend void read(const Socket& socket, ArrayT<T, R...>& array); + + Socket(Socket&&) = default; + Socket(const Socket&) = default; + + ~Socket() = default; +}; + +Socket createServerSocket(int port_number); +Socket acceptClientSocket(const Socket& server); +Socket connectServerSocket(const std::string& server_name, int port_number); + +template <typename T> +inline void +write(const Socket& socket, const T& value) +{ + static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type"); + socket._write(reinterpret_cast<const char*>(&value), sizeof(T) / sizeof(char)); +} + +template <template <typename T, typename... R> typename ArrayT, typename T, typename... R> +void +write(const Socket& socket, const ArrayT<T, R...>& array) +{ + socket._write(reinterpret_cast<const char*>(&array[0]), array.size() * sizeof(T) / sizeof(char)); +} + +template <typename T> +inline void +read(const Socket& socket, T& value) +{ + static_assert(not std::is_const_v<T>, "cannot read values into const data"); + socket._read(reinterpret_cast<char*>(&value), sizeof(T) / sizeof(char)); +} + +template <template <typename T, typename... R> typename ArrayT, typename T, typename... R> +void +read(const Socket& socket, ArrayT<T, R...>& array) +{ + static_assert(not std::is_const_v<T>, "cannot read values into const data"); + static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type"); + if (array.size() > 0) { + socket._read(reinterpret_cast<char*>(&array[0]), array.size() * sizeof(T) / sizeof(char)); + } +} + +#endif // SOCKET_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 96729d8acb0fc5dc87df0ddb732d63fc0d2a8ee7..6bccbfae744ed002e0edcde321205f309e88105a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -120,6 +120,8 @@ add_executable (unit_tests test_RevisionInfo.cpp test_SmallArray.cpp test_SmallVector.cpp + test_Socket.cpp + test_SocketModule.cpp test_SquareGaussQuadrature.cpp test_SquareTransformation.cpp test_SymbolTable.cpp diff --git a/tests/test_BinaryExpressionProcessor_shift.cpp b/tests/test_BinaryExpressionProcessor_shift.cpp index 2f89f1f61c45ddcc137434d28f3196201c1ce866..c1892a2ff3a793a1cdc9a48e6f1a454b090fe77d 100644 --- a/tests/test_BinaryExpressionProcessor_shift.cpp +++ b/tests/test_BinaryExpressionProcessor_shift.cpp @@ -5,6 +5,7 @@ #include <utils/pugs_config.hpp> #include <fstream> +#include <netdb.h> #include <unistd.h> // clazy:excludeall=non-pod-global-static @@ -20,8 +21,10 @@ TEST_CASE("BinaryExpressionProcessor shift", "[language]") { std::ostringstream data; + data << R"(import socket;)"; data << "let fout:ostream, fout = ofstream(\"" << path.string() << "\");\n"; - data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";)"; + data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n"; +fout << createSocketServer(0);)"; TAO_PEGTL_NAMESPACE::string_input input{data.str(), "test.pgs"}; auto ast = ASTBuilder::build(input); @@ -53,7 +56,27 @@ TEST_CASE("BinaryExpressionProcessor shift", "[language]") } } while (fin); - REQUIRE(file_content == "2 true 5\n"); + std::string expected = "2 true 5\n"; + char hbuf[NI_MAXHOST]; + ::gethostname(hbuf, NI_MAXHOST); + expected += hbuf; + expected += ':'; + + REQUIRE(file_content.size() > expected.size()); + REQUIRE(file_content.substr(0, expected.size()) == expected); + + std::string suffix = file_content.substr(expected.size(), file_content.size() - expected.size()); + + auto is_int = [](const std::string& s) { + for (const char& c : s) { + if (not std::isdigit(c)) { + return false; + } + } + return true; + }; + + REQUIRE(is_int(suffix)); } std::filesystem::remove(filename); diff --git a/tests/test_MathModule.cpp b/tests/test_MathModule.cpp index 6846b7c4e24a17dde8747afd9e4fcb1982678985..99801b5f1d6602cbc40b0308e20ea1c1d9009b24 100644 --- a/tests/test_MathModule.cpp +++ b/tests/test_MathModule.cpp @@ -4,8 +4,6 @@ #include <language/modules/MathModule.hpp> #include <language/utils/BuiltinFunctionEmbedder.hpp> -#include <set> - // clazy:excludeall=non-pod-global-static TEST_CASE("MathModule", "[language]") diff --git a/tests/test_Socket.cpp b/tests/test_Socket.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3293f6b8d7fe18db31a0db1aaac0ae30474d8808 --- /dev/null +++ b/tests/test_Socket.cpp @@ -0,0 +1,116 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/PugsAssert.hpp> + +#include <utils/Socket.hpp> + +#include <sstream> +#include <thread> + +#include <netdb.h> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("Socket", "[utils]") +{ + SECTION("create/connect and simple read/write") + { + auto self_client = [](int port) { + Socket self_server = connectServerSocket("localhost", port); + std::vector<int> values = {1, 2, 4, 7}; + write(self_server, values.size()); + write(self_server, values); + }; + + Socket server = createServerSocket(0); + + std::thread t(self_client, server.portNumber()); + + Socket client = acceptClientSocket(server); + + size_t vector_size = [&] { + size_t vector_size; + read(client, vector_size); + return vector_size; + }(); + + REQUIRE(vector_size == 4); + + std::vector<int> v(vector_size); + read(client, v); + + REQUIRE(v == std::vector<int>{1, 2, 4, 7}); + + t.join(); + } + + SECTION("move constructor") + { + Socket server = createServerSocket(0); + int port_number = server.portNumber(); + + Socket moved_server(std::move(server)); + + REQUIRE(port_number == moved_server.portNumber()); + } + + SECTION("host and port info") + { + Socket server = createServerSocket(0); + int port_number = server.portNumber(); + + std::ostringstream info; + info << server; + + std::ostringstream expected; + char hbuf[NI_MAXHOST]; + ::gethostname(hbuf, NI_MAXHOST); + expected << hbuf << ':' << port_number; + + REQUIRE(info.str() == expected.str()); + } + + SECTION("errors") + { + SECTION("connection") + { + { + auto server = createServerSocket(0); + REQUIRE_THROWS_WITH(createServerSocket(server.portNumber()), "error: Address already in use"); + } + REQUIRE_THROWS_WITH(connectServerSocket("localhost", 1), "error: Connection refused"); + + // The error message is not checked since it can depend on the + // network connection itself + REQUIRE_THROWS(connectServerSocket("an invalid host name", 1)); + } + + SECTION("server <-> server") + { + Socket server = createServerSocket(0); + REQUIRE_THROWS_WITH(write(server, 12), "error: Server cannot write to server socket"); + REQUIRE_THROWS_WITH(write(server, std::vector<int>{1, 2, 3}), "error: Server cannot write to server socket"); + + double x; + REQUIRE_THROWS_WITH(read(server, x), "error: Server cannot read from server socket"); + std::vector<double> v(1); + REQUIRE_THROWS_WITH(read(server, v), "error: Server cannot read from server socket"); + } + + SECTION("invalid read") + { + auto self_client = [](int port) { Socket self_server = connectServerSocket("localhost", port); }; + + Socket server = createServerSocket(0); + + std::thread t(self_client, server.portNumber()); + Socket client = acceptClientSocket(server); + t.join(); + + double x; + REQUIRE_THROWS_WITH(read(client, x), "error: Could not read data"); + // One cannot test easily write errors... + } + } +} diff --git a/tests/test_SocketModule.cpp b/tests/test_SocketModule.cpp new file mode 100644 index 0000000000000000000000000000000000000000..879595965742b5aaa100784037506e58db22150f --- /dev/null +++ b/tests/test_SocketModule.cpp @@ -0,0 +1,711 @@ +#include <catch2/catch_approx.hpp> +#include <catch2/catch_test_macros.hpp> + +#include <language/modules/SocketModule.hpp> +#include <language/utils/BuiltinFunctionEmbedder.hpp> +#include <language/utils/OperatorRepository.hpp> +#include <utils/Socket.hpp> + +#include <thread> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("SocketModule", "[language]") +{ + SocketModule socket_module; + const auto& name_builtin_function = socket_module.getNameBuiltinFunctionMap(); + + REQUIRE(name_builtin_function.size() == 25); + + auto create_socket_server = [&name_builtin_function] { + auto i_function = name_builtin_function.find("createSocketServer:N"); + REQUIRE(i_function != name_builtin_function.end()); + + uint64_t arg = 0; + DataVariant arg_variant = arg; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get())); + + return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr(); + }; + + auto accept_socket_client = [&name_builtin_function](const std::shared_ptr<const Socket>& server) { + auto i_function = name_builtin_function.find("acceptSocketClient:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(server)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get())); + + return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr(); + }; + + auto connect_socket_server = [&name_builtin_function](const std::string& hostname, int64_t port) { + auto i_function = name_builtin_function.find("connectSocketServer:string*N"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant hostname_variant = hostname; + DataVariant port_variant = port; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({hostname_variant, port_variant}); + + REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get())); + + return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr(); + }; + + SECTION("read") + { + SECTION("read_B") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + bool b = true; + write(*self_server, b); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_B:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<bool>(result_variant) == true); + + t.join(); + } + + SECTION("read_N") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + uint64_t n = 12; + write(*self_server, n); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_N:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<uint64_t>(result_variant) == 12); + + t.join(); + } + + SECTION("read_Z") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + int64_t k = -3; + write(*self_server, k); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_Z:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<int64_t>(result_variant) == -3); + + t.join(); + } + + SECTION("read_R") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + double x = -3.141; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<double>(result_variant) == -3.141); + + t.join(); + } + + SECTION("read_R1") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<1> x{1.414}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R1:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyVector<1>>(result_variant) == TinyVector<1>{1.414}); + + t.join(); + } + + SECTION("read_R2") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<2> x{1.414, -3.7}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R2:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyVector<2>>(result_variant) == TinyVector<2>{1.414, -3.7}); + + t.join(); + } + + SECTION("read_R3") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<3> x{1.414, -3.7, 5.19}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R3:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyVector<3>>(result_variant) == TinyVector<3>{1.414, -3.7, 5.19}); + + t.join(); + } + + SECTION("read_R1x1") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<1> x{1.414}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R1x1:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyMatrix<1>>(result_variant) == TinyMatrix<1>{1.414}); + + t.join(); + } + + SECTION("read_R2x2") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<2> x{1.414, -3.7, 2.4, -6}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R2x2:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyMatrix<2>>(result_variant) == TinyMatrix<2>{1.414, -3.7, 2.4, -6}); + + t.join(); + } + + SECTION("read_R3x3") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<3> x{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3}; + write(*self_server, x); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_R3x3:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<TinyMatrix<3>>(result_variant) == TinyMatrix<3>{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3}); + + t.join(); + } + + SECTION("read_string") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + std::string s = "foobar"; + write(*self_server, s.size()); + write(*self_server, s); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("read_string:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + + REQUIRE(std::get<std::string>(result_variant) == std::string{"foobar"}); + + t.join(); + } + } + + SECTION("write") + { + auto get_result = [&name_builtin_function](std::shared_ptr<const Socket> p_client) { + auto i_function = name_builtin_function.find("read_B:socket"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({arg_variant}); + return std::get<bool>(result_variant); + }; + + SECTION("B") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + bool b; + read(*self_server, b); + const bool result = (b == true); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*B"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = true; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("N") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + uint64_t n; + read(*self_server, n); + const bool result = (n == 17); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*N"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = uint64_t{17}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("Z") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + int64_t k; + read(*self_server, k); + const bool result = (k == -13); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*Z"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = int64_t{-13}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + double x; + read(*self_server, x); + const bool result = (x == -3.1415); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = double{-3.1415}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^1") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<1> x; + read(*self_server, x); + const bool result = (x == TinyVector<1>{-3.1415}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^1"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyVector<1>{-3.1415}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^2") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<2> x; + read(*self_server, x); + const bool result = (x == TinyVector<2>{-3.1415, 1.414}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^2"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyVector<2>{-3.1415, 1.414}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^3") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyVector<3> x; + read(*self_server, x); + const bool result = (x == TinyVector<3>{-3.1415, 1.414, 1.3}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^3"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyVector<3>{-3.1415, 1.414, 1.3}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^1x1") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<1> x; + read(*self_server, x); + const bool result = (x == TinyMatrix<1>{-3.1415}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^1x1"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyMatrix<1>{-3.1415}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^2x2") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<2> x; + read(*self_server, x); + const bool result = (x == TinyMatrix<2>{3, -3.1415, -1.2, 5.6}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^2x2"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyMatrix<2>{3, -3.1415, -1.2, 5.6}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("R^3x3") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + TinyMatrix<3> x; + read(*self_server, x); + const bool result = (x == TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2}); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*R^3x3"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + + SECTION("string") + { + std::shared_ptr p_server = create_socket_server(); + + auto self_client = [&connect_socket_server](int port) { + std::shared_ptr self_server = connect_socket_server("localhost", port); + + size_t size; + read(*self_server, size); + std::string s; + s.resize(size); + read(*self_server, s); + const bool result = (s == "foobar"); + write(*self_server, result); + }; + std::thread t(self_client, p_server->portNumber()); + + std::shared_ptr p_client = accept_socket_client(p_server); + + auto i_function = name_builtin_function.find("write:socket*string"); + REQUIRE(i_function != name_builtin_function.end()); + + DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client)); + DataVariant value_variant = std::string{"foobar"}; + + IBuiltinFunctionEmbedder& function_embedder = *i_function->second; + DataVariant result_variant = function_embedder.apply({socket_variant, value_variant}); + + REQUIRE(get_result(p_client)); + + t.join(); + } + } +}