commit e9b8aa88c09d0748fef6fff0e850ec5c1295b232 Author: Alex Yatskov Date: Sun Aug 28 08:45:42 2011 -0700 initial drop diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..31ce17b --- /dev/null +++ b/main.cpp @@ -0,0 +1,33 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" + +using namespace metacall; + +int main(int argc, char **argv) { + Client client; + return 0; +} diff --git a/mc_binding-inl.hpp b/mc_binding-inl.hpp new file mode 100644 index 0000000..5f16fae --- /dev/null +++ b/mc_binding-inl.hpp @@ -0,0 +1,134 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace metacall { + + +// +// Binding +// + +template +bool Binding::bind(const Token& token, R(*function)()) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor0p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor1p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor2p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor3p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor4p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4, P5)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor5p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4, P5, P6)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor6p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor7p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7, P8)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor8p(function); + return true; + } + + return false; +} + +template +bool Binding::bind(const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7, P8, P9)) { + if (functors_.find(token) == functors_.end()) { + functors_[token] = new Functor9p(function); + return true; + } + + return false; +} + + +} diff --git a/mc_binding.cpp b/mc_binding.cpp new file mode 100644 index 0000000..750f7a0 --- /dev/null +++ b/mc_binding.cpp @@ -0,0 +1,69 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" + +namespace metacall { + + +// +// Binding +// + +Binding::~Binding() { + unbindAll(); +} + +Binding::CallResult Binding::call(const Token& token, Deserializer* args, Serializer* ret) const { + const FunctorMap::const_iterator iter = functors_.find(token); + + if (iter == functors_.end()) { + return CALL_RESULT_UNBOUND_FUNC; + } + if (!iter->second->call(args, ret)) { + return CALL_RESULT_INVALID_ARGS; + } + + return CALL_RESULT_SUCCESS; +} + +void Binding::unbind(const Token& token) { + FunctorMap::iterator iter = functors_.find(token); + if (iter != functors_.end()) { + delete iter->second; + functors_.erase(iter); + } +} + +void Binding::unbindAll() { + for (FunctorMap::iterator iter = functors_.begin(); iter != functors_.end(); ++iter) { + delete iter->second; + } + + functors_.clear(); +} + + +} diff --git a/mc_binding.hpp b/mc_binding.hpp new file mode 100644 index 0000000..03e5e58 --- /dev/null +++ b/mc_binding.hpp @@ -0,0 +1,76 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Binding +// + +class Binding { +public: + enum CallResult { + CALL_RESULT_SUCCESS, + CALL_RESULT_INVALID_ARGS, + CALL_RESULT_UNBOUND_FUNC + }; + + ~Binding(); + + template + bool bind (const Token& token, R(*function)()); + template + bool bind (const Token& token, R(*function)(P1)); + template + bool bind (const Token& token, R(*function)(P1, P2)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4, P5)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4, P5, P6)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7, P8)); + template + bool bind (const Token& token, R(*function)(P1, P2, P3, P4, P5, P6, P7, P8, P9)); + + CallResult call (const Token& token, Deserializer* args, Serializer* ret) const; + void unbind (const Token& token); + void unbindAll (); + +private: + typedef std::map FunctorMap; + FunctorMap functors_; +}; + + +} diff --git a/mc_buffer.cpp b/mc_buffer.cpp new file mode 100644 index 0000000..c9afaeb --- /dev/null +++ b/mc_buffer.cpp @@ -0,0 +1,102 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_buffer.hpp" + +namespace metacall { + + +// +// Buffer +// + +Buffer::Buffer(const void* data, int count) { + setData(data, count); +} + +Buffer::Buffer() { + +} + +void Buffer::addToBack(const void* data, int bytes) { + data_.insert( + data_.end(), + static_cast(data), + static_cast(data) + bytes + ); +} + +void Buffer::addToFront(const void* data, int bytes) { + data_.insert( + data_.begin(), + static_cast(data), + static_cast(data) + bytes + ); +} + +int Buffer::removeFromFront(void* data, int bytes) { + bytes = std::min(static_cast(bytes), data_.size()); + if (data != NULL) { + memcpy(data, &data_[0], bytes); + } + data_.erase(data_.begin(), data_.begin() + bytes); + return bytes; +} + +int Buffer::removeFromBack(void* data, int bytes) { + bytes = std::min(static_cast(bytes), data_.size()); + if (data != NULL) { + memcpy(data, &data_[data_.size() - bytes], bytes); + } + data_.erase(data_.end() - bytes, data_.end()); + return bytes; +} + +void Buffer::clear() { + data_.clear(); +} + +void Buffer::setData(const void* data, int bytes) { + data_.assign( + static_cast(data), + static_cast(data) + bytes + ); +} + +const void* Buffer::data() const { + return &data_[0]; +} + +void* Buffer::data() { + return &data_[0]; +} + +int Buffer::bytes() const { + return data_.size(); +} + + +} diff --git a/mc_buffer.hpp b/mc_buffer.hpp new file mode 100644 index 0000000..b71f247 --- /dev/null +++ b/mc_buffer.hpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Buffer +// + +class Buffer { +public: + Buffer(const void* data, int bytes); + Buffer(); + + void addToBack (const void* data, int bytes); + void addToFront (const void* data, int bytes); + int removeFromFront (void* data, int bytes); + int removeFromBack (void* data, int bytes); + void clear (); + + void setData (const void* data, int bytes); + const void* data () const; + void* data (); + int bytes () const; + +private: + std::vector data_; +}; + + +} diff --git a/mc_client.cpp b/mc_client.cpp new file mode 100644 index 0000000..1174c26 --- /dev/null +++ b/mc_client.cpp @@ -0,0 +1,97 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_client.hpp" + +namespace metacall { + + +// +// Client +// + +Client::Client() : + stream_(&socket_), + protocol_(&stream_, &binding_) +{ +} + +void Client::advance() { + if (!socket_.opened()) { + return; + } + + if (socket_.connected()) { + protocol_.advance(); + } + else { + disconnect(); + } +} + +bool Client::connect(const char name[], int port) { + disconnect(); + + socket_.open(); + socket_.setNagle(false); + + if (!socket_.connect(name, port)) { + disconnect(); + return false; + } + + socket_.setBlocking(false); + return true; +} + +void Client::disconnect() { + protocol_.clearHandlers(); + stream_.reset(); + socket_.close(); +} + +bool Client::connected() const { + return socket_.connected(); +} + +const Protocol* Client::protocol() const { + return &protocol_; +} + +Protocol* Client::protocol() { + return &protocol_; +} + +const Binding* Client::binding() const { + return &binding_; +} + +Binding* Client::binding() { + return &binding_; +} + + +} diff --git a/mc_client.hpp b/mc_client.hpp new file mode 100644 index 0000000..58b53b2 --- /dev/null +++ b/mc_client.hpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Client +// + +class Client { +public: + Client(); + + void advance (); + + bool connect (const char name[], int port); + void disconnect (); + bool connected () const; + + const Protocol* protocol () const; + Protocol* protocol (); + const Binding* binding () const; + Binding* binding (); + +private: + Socket socket_; + Stream stream_; + Binding binding_; + Protocol protocol_; +}; + + +} diff --git a/mc_common.hpp b/mc_common.hpp new file mode 100644 index 0000000..7c9c57b --- /dev/null +++ b/mc_common.hpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Macros +// + +#define IS_TRUE(x) ((x) != 0) +#define ASSERT assert + + +// +// Types +// + +#ifdef _WIN32 + typedef unsigned __int8 byte; + typedef unsigned __int16 word; + typedef unsigned __int32 dword; + typedef unsigned __int64 qword; +#else + typedef unsigned char byte; + typedef unsigned short word; + typedef unsigned int dword; + typedef unsigned long int qword; +#endif + + +} diff --git a/mc_functor.hpp b/mc_functor.hpp new file mode 100644 index 0000000..f478e16 --- /dev/null +++ b/mc_functor.hpp @@ -0,0 +1,755 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// IFunctor +// + +class IFunctor { +public: + virtual bool call(Deserializer* args, Serializer* ret) const = 0; +}; + + +// +// Functor0p +// + +template +class Functor0p : public IFunctor { +public: + typedef R(*F)(); + + Functor0p(F function) : + function_(function) + { + } + + bool call(Deserializer*, Serializer* ret) const { + ret->write(function_()); + return true; + } + +private: + F function_; +}; + +template <> +class Functor0p : public IFunctor { +public: + typedef void(*F)(); + + Functor0p(F function) : + function_(function) + { + } + + bool call(Deserializer*, Serializer*) const { + function_(); + return true; + } + +private: + F function_; +}; + + +// +// Functor1p +// + +template +class Functor1p : public IFunctor { +public: + typedef R (*F)(P1); + + Functor1p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + + const bool success = args->read(&p1); + if (success) { + ret->write(function_(p1)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor1p : public IFunctor { +public: + typedef void (*F)(P1); + + Functor1p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + + const bool success = args->read(&p1); + if (success) { + function_(p1); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor2p +// + +template +class Functor2p : public IFunctor { +public: + typedef R (*F)(P1, P2); + + Functor2p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + + const bool success = + args->read(&p1) && + args->read(&p2); + + if (success) { + ret->write(function_(p1, p2)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor2p : public IFunctor { +public: + typedef void (*F)(P1, P2); + + Functor2p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + + const bool success = + args->read(&p1) && + args->read(&p2); + + if (success) { + function_(p1, p2); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor3p +// + +template +class Functor3p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3); + + Functor3p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3); + + if (success) { + ret->write(function_(p1, p2, p3)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor3p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3); + + Functor3p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3); + + if (success) { + function_(p1, p2, p3); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor4p +// + +template +class Functor4p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4); + + Functor4p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4); + + if (success) { + ret->write(function_(p1, p2, p3, p4)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor4p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4); + + Functor4p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4); + + if (success) { + function_(p1, p2, p3, p4); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor5p +// + +template +class Functor5p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4, P5); + + Functor5p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5); + + if (success) { + ret->write(function_(p1, p2, p3, p4, p5)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor5p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4, P5); + + Functor5p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5); + + if (success) { + function_(p1, p2, p3, p4, p5); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor6p +// + +template +class Functor6p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4, P5, P6); + + Functor6p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6); + + if (success) { + ret->write(function_(p1, p2, p3, p4, p5, p6)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor6p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4, P5, P6); + + Functor6p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6); + + if (success) { + function_(p1, p2, p3, p4, p5, p6); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor7p +// + +template +class Functor7p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4, P5, P6, P7); + + Functor7p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7); + + if (success) { + ret->write(function_(p1, p2, p3, p4, p5, p6, p7)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor7p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4, P5, P6, P7); + + Functor7p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7); + + if (success) { + function_(p1, p2, p3, p4, p5, p6, p7); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor8p +// + +template +class Functor8p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4, P5, P6, P7, P8); + + Functor8p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + typename StripConstRef::Type p8; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7) && + args->read(&p8); + + if (success) { + ret->write(function_(p1, p2, p3, p4, p5, p6, p7, p8)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor8p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4, P5, P6, P7, P8); + + Functor8p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + typename StripConstRef::Type p8; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7) && + args->read(&p8); + + if (success) { + function_(p1, p2, p3, p4, p5, p6, p7, p8); + } + + return success; + } + +private: + F function_; +}; + + +// +// Functor9p +// + +template +class Functor9p : public IFunctor { +public: + typedef R (*F)(P1, P2, P3, P4, P5, P6, P7, P8, P9); + + Functor9p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer* ret) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + typename StripConstRef::Type p8; + typename StripConstRef::Type p9; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7) && + args->read(&p8) && + args->read(&p9); + + if (success) { + ret->write(function_(p1, p2, p3, p4, p5, p6, p7, p8, p9)); + } + + return success; + } + +private: + F function_; +}; + +template +class Functor9p : public IFunctor { +public: + typedef void (*F)(P1, P2, P3, P4, P5, P6, P7, P8, P9); + + Functor9p(F function) : + function_(function) + { + } + + bool call(Deserializer* args, Serializer*) const { + typename StripConstRef::Type p1; + typename StripConstRef::Type p2; + typename StripConstRef::Type p3; + typename StripConstRef::Type p4; + typename StripConstRef::Type p5; + typename StripConstRef::Type p6; + typename StripConstRef::Type p7; + typename StripConstRef::Type p8; + typename StripConstRef::Type p9; + + const bool success = + args->read(&p1) && + args->read(&p2) && + args->read(&p3) && + args->read(&p4) && + args->read(&p5) && + args->read(&p6) && + args->read(&p7) && + args->read(&p8) && + args->read(&p9); + + if (success) { + function_(p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + return success; + } + +private: + F function_; +}; + + +} diff --git a/mc_meta.hpp b/mc_meta.hpp new file mode 100644 index 0000000..5f77594 --- /dev/null +++ b/mc_meta.hpp @@ -0,0 +1,88 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Meta +// + +qword serialize (class Serializer*, ...); +qword deserialize (class Deserializer*, ...); + +template +struct IsSerializable { + enum { + Value = sizeof(bool) == sizeof( + serialize( + static_cast(0), + *static_cast(0) + ) + ) + }; +}; + +template +struct IsDeserializable { + enum { + Value = sizeof(bool) == sizeof( + deserialize( + static_cast(0), + static_cast(0) + ) + ) + }; +}; + +template +struct StripRef { + typedef T Type; +}; + +template +struct StripRef { + typedef T Type; +}; + +template +struct StripConst { + typedef T Type; +}; + +template +struct StripConst { + typedef T Type; +}; + +template +struct StripConstRef { + typedef typename StripConst::Type >::Type Type; +}; + + +} diff --git a/mc_packet.hpp b/mc_packet.hpp new file mode 100644 index 0000000..a15185c --- /dev/null +++ b/mc_packet.hpp @@ -0,0 +1,112 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Constants +// + +enum PacketConst { + PACKET_CONST_MAGIC = 0x55534544, + PACKET_CONST_VERSION = 1 +}; + +enum PacketId { + PACKET_ID_INVALID, + PACKET_ID_FUNCTION_CALL_REQUEST, + PACKET_ID_FUNCTION_CALL_REPLY +}; + + +// +// PacketHeader +// + +struct PacketHeader { + PacketHeader() : + magic(0), + id(PACKET_ID_INVALID), + size(0) + { + } + + dword magic; + byte id; + dword size; +}; + + +// +// PacketInvokeRequest +// + +struct PacketInvokeRequest { + PacketInvokeRequest() : + function(0), + taskId(0) + { + } + + enum { + Id = PACKET_ID_FUNCTION_CALL_REQUEST + }; + + dword function; + dword taskId; + Buffer data; +}; + + +// +// PacketInvokeReply +// + +struct PacketInvokeReply { + PacketInvokeReply() : + flags(0), + taskId(0) + { + } + + enum { + Id = PACKET_ID_FUNCTION_CALL_REPLY + }; + + enum { + FLAG_UNBOUND_FUNC = 1 << 0, + FLAG_INVALID_ARGS = 1 << 1, + }; + + word flags; + dword taskId; + Buffer data; +}; + + +} diff --git a/mc_protocol-inl.hpp b/mc_protocol-inl.hpp new file mode 100644 index 0000000..f5f033f --- /dev/null +++ b/mc_protocol-inl.hpp @@ -0,0 +1,174 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace metacall { + + +template +TaskId Protocol::invoke(const Token& token, const P1& p1) { + PacketInvokeRequest packetRequest; + + Serializer serializer(&packetRequest.data); + serializer.write(p1); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2) { + PacketInvokeRequest packetRequest; + + Serializer serializer(&packetRequest.data); + serializer.write(p1); + serializer.write(p2); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3) { + PacketInvokeRequest packetRequest; + + Serializer serializer(&packetRequest.data); + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3 & p3, const P4& p4) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + serializer.write(p5); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + serializer.write(p5); + serializer.write(p6); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + serializer.write(p5); + serializer.write(p6); + serializer.write(p7); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + serializer.write(p5); + serializer.write(p6); + serializer.write(p7); + serializer.write(p8); + + return invokeExec(token, &packetRequest); +} + +template +TaskId Protocol::invoke(const Token& token, const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6, const P7& p7, const P8& p8, const P9& p9) { + PacketInvokeRequest packetRequest; + Serializer serializer(&packetRequest.data); + + serializer.write(p1); + serializer.write(p2); + serializer.write(p3); + serializer.write(p4); + serializer.write(p5); + serializer.write(p6); + serializer.write(p7); + serializer.write(p8); + serializer.write(p9); + + return invokeExec(token, &packetRequest); +} + +template +TaskState Protocol::queryResult(TaskId id, R* result) { + TaskMap::const_iterator iter = taskMap_.find(id); + if (iter == taskMap_.end()) { + return TASK_STATE_UNDEFINED; + } + + TaskEntry& entry = iter->second; + if (entry.state == TASK_STATE_READY) { + Deserializer deserializer(&entry.data); + if (!deserializer.read(result)) { + entry.state = TASK_STATE_ERROR; + } + } + + return entry.state; +} + + +} diff --git a/mc_protocol.cpp b/mc_protocol.cpp new file mode 100644 index 0000000..9ed98a9 --- /dev/null +++ b/mc_protocol.cpp @@ -0,0 +1,208 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_protocol.hpp" + +namespace metacall { + + +// +// Protocol +// + +Protocol::Protocol(Stream* stream, Binding* binding) : + stream_(stream), + binding_(binding), + rate_(64) +{ +} + +void Protocol::advance() { + if (!stream_->socket()->connected()) { + return; + } + + for (int i = 0; i < rate_; ++i) { + if (advanceStream() != Stream::STATE_READY) { + break; + } + } + + std::vector processed; + for (TaskMap::iterator iter = taskMap_.begin(); iter != taskMap_.end(); ++iter) { + TaskEntry& entry = iter->second; + switch (entry.state) { + case TASK_STATE_READY: + case TASK_STATE_ERROR: + processed.push_back(iter); + for (size_t i = 0; i < entry.handlers.size(); ++i) { + HandlerEntry& handler = entry.handlers[i]; + handler.handler(this, iter->first, entry.state, handler.userPtr); + } + break; + default: + break; + } + } + + for (size_t i = 0; i < processed.size(); ++i) { + taskMap_.erase(processed[i]); + } +} + +void Protocol::setRate(int rate) { + rate_ = rate; +} + +bool Protocol::setHandler(TaskId id, HandlerProc handler, void* userPtr) { + const TaskMap::iterator iter = taskMap_.find(id); + if (iter == taskMap_.end() || iter->second.state != TASK_STATE_PENDING) { + return false; + } + + iter->second.handlers.push_back(HandlerEntry(handler, userPtr)); + return true; +} + +void Protocol::clearHandler(TaskId id) { + const TaskMap::iterator iter = taskMap_.find(id); + if (iter != taskMap_.end()) { + taskMap_.erase(iter); + } +} + +void Protocol::clearHandlers() { + for (TaskMap::iterator iter = taskMap_.begin(); iter != taskMap_.end(); ++iter) { + iter->second.handlers.clear(); + } +} + +TaskState Protocol::queryState(TaskId id) const { + const TaskMap::const_iterator iter = taskMap_.find(id); + return iter == taskMap_.end() ? TASK_STATE_UNDEFINED : iter->second.state; +} + +Stream::State Protocol::advanceStream() { + stream_->advance(); + + PacketHeader header; + int headerSize = 0; + + Stream::State state = stream_->peek(&header, &headerSize); + if (state == Stream::STATE_READY) { + switch (header.id) { + case PACKET_ID_FUNCTION_CALL_REQUEST: + state = advanceInvokeRequest(); + break; + case PACKET_ID_FUNCTION_CALL_REPLY: + state = advanceInvokeReply(); + break; + default: + state = Stream::STATE_ERROR_PROTOCOL; + break; + } + } + + return state; +} + +Stream::State Protocol::advanceInvokeReply() { + PacketInvokeReply packetReply; + Deserializer deserializer(&packetReply.data); + + Stream::State state = stream_->receive(&packetReply); + if (state == Stream::STATE_READY) { + const TaskMap::iterator iter = taskMap_.find(static_cast(packetReply.taskId)); + if (iter == taskMap_.end()) { + state = Stream::STATE_ERROR_PROTOCOL; + } + else { + const bool error = + IS_TRUE(packetReply.flags & PacketInvokeReply::FLAG_UNBOUND_FUNC) || + IS_TRUE(packetReply.flags & PacketInvokeReply::FLAG_INVALID_ARGS); + + iter->second.state = error ? TASK_STATE_ERROR : TASK_STATE_READY; + iter->second.data = packetReply.data; + } + } + + return state; +} + +Stream::State Protocol::advanceInvokeRequest() { + PacketInvokeRequest packetRequest; + Deserializer deserializer(&packetRequest.data); + + const Stream::State state = stream_->receive(&packetRequest); + if (state == Stream::STATE_READY) { + PacketInvokeReply packetReply; + packetReply.taskId = packetRequest.taskId; + + Serializer serializer(&packetReply.data); + switch (binding_->call(packetRequest.function, &deserializer, &serializer)) { + case Binding::CALL_RESULT_INVALID_ARGS: + packetReply.flags |= PacketInvokeReply::FLAG_INVALID_ARGS; + break; + case Binding::CALL_RESULT_UNBOUND_FUNC: + packetReply.flags |= PacketInvokeReply::FLAG_UNBOUND_FUNC; + break; + default: + break; + } + + stream_->send(packetReply); + } + + return state; +} + +TaskId Protocol::invoke(const Token& token) { + PacketInvokeRequest packetRequest; + return invokeExec(token, &packetRequest); +} + +TaskId Protocol::invokeExec(const Token& token, PacketInvokeRequest* packetRequest) { + const TaskId taskId = registerTaskId(); + + packetRequest->function = token; + packetRequest->taskId = taskId; + + if (stream_->send(*packetRequest) != Stream::STATE_READY) { + return TASK_ID_INVALID; + } + + taskMap_[taskId] = TaskEntry(); + return taskId; +} + +TaskId Protocol::registerTaskId() { + static int id = TASK_ID_INVALID; + while (++id == TASK_ID_INVALID); + return static_cast(id); +} + + +} diff --git a/mc_protocol.hpp b/mc_protocol.hpp new file mode 100644 index 0000000..9e7de94 --- /dev/null +++ b/mc_protocol.hpp @@ -0,0 +1,132 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Types +// + +enum TaskId { + TASK_ID_INVALID +}; + +enum TaskState { + TASK_STATE_UNDEFINED, + TASK_STATE_PENDING, + TASK_STATE_READY, + TASK_STATE_ERROR +}; + +typedef void (*HandlerProc)( + class Protocol* protocol, + TaskId id, + TaskState state, + void* userPtr +); + + +// +// Protocol +// + +class Protocol { +public: + Protocol(Stream* stream, Binding* binding); + + void advance (); + + void setRate (int rate); + bool setHandler (TaskId id, HandlerProc handler, void* userPtr = NULL); + void clearHandler (TaskId id); + void clearHandlers (); + + template + TaskState queryResult (TaskId id, R* result); + TaskState queryState (TaskId id) const; + + TaskId invoke (const Token& token); + template + TaskId invoke (const Token& token, const P1& p0); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3, const P5& p4); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3, const P5& p4, const P6& p5); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3, const P5& p4, const P6& p5, const P7& p6); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3, const P5& p4, const P6& p5, const P7& p6, const P8& p7); + template + TaskId invoke (const Token& token, const P1& p0, const P2& p1, const P3& p2, const P4& p3, const P5& p4, const P6& p5, const P7& p6, const P8& p7, const P9& p8); + +private: + struct HandlerEntry { + HandlerEntry(HandlerProc handler, void* userPtr) : + handler(handler), + userPtr(userPtr) + { + } + + HandlerProc handler; + void* userPtr; + }; + + struct TaskEntry { + TaskEntry() : + state(TASK_STATE_PENDING) + { + } + + typedef std::vector HandlerList; + HandlerList handlers; + TaskState state; + Buffer data; + }; + + Stream::State advanceStream (); + Stream::State advanceInvokeRequest(); + Stream::State advanceInvokeReply (); + + TaskId invokeExec (const Token& token, PacketInvokeRequest* packetRequest); + static TaskId registerTaskId (); + + typedef std::map TaskMap; + TaskMap taskMap_; + Stream* stream_; + Binding* binding_; + int rate_; +}; + + +} diff --git a/mc_serial-inl.hpp b/mc_serial-inl.hpp new file mode 100644 index 0000000..9d8c546 --- /dev/null +++ b/mc_serial-inl.hpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace metacall { + + +// +// TypeReader +// + +template +struct TypeReader { + static bool read(Deserializer* deserializer, T* data) { + return deserialize(deserializer, data); + } +}; + +template +struct TypeReader { + static bool read(Deserializer* deserializer, T* data) { + const T* const temp = reinterpret_cast(deserializer->readRaw(sizeof(T))); + if (temp != NULL) { + *data = *temp; + } + return temp != NULL; + } +}; + +template +bool Deserializer::read(T* data) { + return TypeReader::Value>::read(this, data); +} + + +// +// TypeWriter +// + +template +struct TypeWriter { + static bool write(Serializer* serializer, const T& data) { + return serialize(serializer, data); + } +}; + +template +struct TypeWriter { + static bool write(Serializer* serializer, const T& data) { + return serializer->writeRaw(&data, sizeof(T)); + } +}; + +template +bool Serializer::write(const T& data) { + return TypeWriter::Value>::write(this, data); +} + + +// +// Serializers +// + +template +bool serialize(Serializer* serializer, const ArrayWriter& writer) { + serializer->write(writer.count); + + for (int i = 0; i < writer.count; ++i) { + serializer->write(writer.data[i]); + } + + return true; +} + +template +bool deserialize(Deserializer* deserializer, ArrayReader* reader) { + reader->clear(); + + if (!deserializer->read(&reader->count)) { + return false; + } + + reader->data = new T[reader->count]; + + for (int i = 0; i < reader->count; ++i) { + if (!deserializer->read(reader->data + i)) { + reader->clear(); + return false; + } + } + + return true; +} + + +} diff --git a/mc_serial.cpp b/mc_serial.cpp new file mode 100644 index 0000000..8154aa8 --- /dev/null +++ b/mc_serial.cpp @@ -0,0 +1,173 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_serial.hpp" + +namespace metacall { + + +// +// Deserializer +// + +Deserializer::Deserializer(const Buffer* data) : + data_(data), + offset_(0) +{ +} + +const byte* Deserializer::readRaw(int size) { + if (data_ == NULL || size > data_->bytes() - offset_) { + return NULL; + } + + const byte* data = static_cast(data_->data()) + offset_; + offset_ += size; + + return data; +} + +int Deserializer::offset() const { + return offset_; +} + +bool Deserializer::setOffset(int offset, bool relative) { + const int offsetNew = relative ? offset_ + offset : offset; + + if (offsetNew >= 0 && offsetNew <= data_->bytes()) { + offset_ = offsetNew; + return true; + } + + return false; +} + + +// +// Serializer +// + +Serializer::Serializer(Buffer* data) : + data_(data), + offset_(0) +{ +} + +bool Serializer::writeRaw(const void* data, int size) { + if (data_ != NULL) { + data_->addToBack(static_cast(data), size); + offset_ += size; + } + + return true; +} + +bool Serializer::setOffset(int offset, bool relative) { + const int offsetNew = relative ? offset_ + offset : offset; + + if (offsetNew >= 0 && offsetNew <= data_->bytes()) { + offset_ = offsetNew; + return true; + } + + return false; +} + +int Serializer::offset() const { + return offset_; +} + + +// +// Serializers +// + +bool serialize(Serializer* serializer, const Buffer& buffer) { + serializer->write(buffer.bytes()); + serializer->writeRaw(buffer.data(), buffer.bytes()); + return true; +} + +bool deserialize(Deserializer* deserializer, Buffer* buffer) { + int count = 0; + if (!deserializer->read(&count)) { + return false; + } + + const byte* const data = deserializer->readRaw(count); + if (data == NULL) + { + return false; + } + + buffer->setData(data, count); + return true; +} + +bool serialize(Serializer* serializer, const PacketHeader& packet) { + return + serializer->write(packet.magic) && + serializer->write(packet.id) && + serializer->write(packet.size); +} + +bool deserialize(Deserializer* deserializer, PacketHeader* packet) { + return + deserializer->read(&packet->magic) && + deserializer->read(&packet->id) && + deserializer->read(&packet->size); +} + +bool serialize(Serializer* serializer, const PacketInvokeRequest& packet) { + return + serializer->write(packet.function) && + serializer->write(packet.taskId) && + serializer->write(packet.data); +} + +bool deserialize(Deserializer* deserializer, PacketInvokeRequest* packet) { + return + deserializer->read(&packet->function) && + deserializer->read(&packet->taskId) && + deserializer->read(&packet->data); +} + +bool serialize(Serializer* serializer, const PacketInvokeReply& packet) { + return + serializer->write(packet.flags) && + serializer->write(packet.taskId) && + serializer->write(packet.data); +} + +bool deserialize(Deserializer* deserializer, PacketInvokeReply* packet) { + return + deserializer->read(&packet->flags) && + deserializer->read(&packet->taskId) && + deserializer->read(&packet->data); +} + + +} diff --git a/mc_serial.hpp b/mc_serial.hpp new file mode 100644 index 0000000..31279c8 --- /dev/null +++ b/mc_serial.hpp @@ -0,0 +1,136 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Deserializer +// + +class Deserializer { +public: + Deserializer(const Buffer* data); + + template + bool read (T* data); + const byte* readRaw (int size); + bool setOffset (int offset, bool relative); + int offset () const; + +private: + const Buffer* data_; + int offset_; +}; + + +// +// Serializer +// + +class Serializer { +public: + Serializer(Buffer* data); + + template + bool write (const T& data); + bool writeRaw (const void* data, int size); + bool setOffset (int offset, bool relative); + int offset () const; + +private: + Buffer* data_; + int offset_; +}; + + +// +// ArrayWriter +// + +template +class ArrayWriter { +public: + ArrayWriter(const T* data, int count) : + data(data), + count(count) + { + } + + const T* data; + int count; +}; + + +// +// ArrayReader +// + +template +class ArrayReader { +public: + ArrayReader() : + data(NULL), + count(0) + { + } + + ~ArrayReader() { + delete[] data; + + data = NULL; + count = 0; + } + + const T* data; + int count; +}; + + +// +// Standard serializers/deserializers +// + +template +bool serialize (Serializer* serializer, const ArrayWriter& writer); +template +bool deserialize (Deserializer* deserializer, ArrayReader* reader); + +bool serialize (Serializer* serializer, const Buffer& buffer); +bool deserialize (Deserializer* deserializer, Buffer* buffer); + +bool serialize (Serializer* serializer, const PacketHeader& packet); +bool deserialize (Deserializer* deserializer, PacketHeader* packet); + +bool serialize (Serializer* serializer, const PacketInvokeRequest& packet); +bool deserialize (Deserializer* deserializer, PacketInvokeRequest* packet); + +bool serialize (Serializer* serializer, const PacketInvokeReply& packet); +bool deserialize (Deserializer* deserializer, PacketInvokeReply* packet); + + +} diff --git a/mc_server.cpp b/mc_server.cpp new file mode 100644 index 0000000..13881ea --- /dev/null +++ b/mc_server.cpp @@ -0,0 +1,169 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_server.hpp" + +namespace metacall { + + +// +// Server +// + +Server::Server() : + clientMax_(1), + clientActive_(CLIENT_ID_INVALID) +{ +} + +Server::~Server() { + disconnectAll(); +} + +void Server::advance() { + advanceConnecting(); + advanceDisconnecting(); + advanceConnected(); +} + +const Binding* Server::binding() const { + return &binding_; +} + +Binding* Server::binding() { + return &binding_; +} + +bool Server::start(int serverPort, int clientsMax) { + stop(); + + if (server_.open() && server_.bind(serverPort) && server_.listen()) { + server_.setBlocking(false); + server_.setNagle(false); + clientMax_ = clientsMax; + return true; + } + + stop(); + return false; +} + +void Server::stop() { + disconnectAll(); + clientMax_ = 0; +} + +void Server::getClients(std::vector* clients) const { + for (ClientMap::const_iterator iter = clients_.begin(); iter != clients_.end(); ++iter) { + clients->push_back(iter->first); + } +} + +bool Server::getClient(ClientId id, ClientData* data) const { + const ClientMap::const_iterator iter = clients_.find(id); + if (iter != clients_.end()) { + data->id = iter->first; + data->name = iter->second->socket.hostname(); + data->protocol = &iter->second->protocol; + return true; + } + + return false; +} + +void Server::disconnect(ClientId id) { + const ClientMap::iterator iter = clients_.find(id); + if (iter != clients_.end()) { + delete iter->second; + clients_.erase(iter); + } +} + +void Server::disconnectAll() { + for (ClientMap::iterator iter = clients_.begin(); iter != clients_.end(); ++iter) { + delete iter->second; + } + + clients_.clear(); +} + +int Server::clientCount() const { + return clients_.size(); +} + +ClientId Server::clientActive() const { + return clientActive_; +} + +void Server::advanceConnecting() { + if (clientMax_ == 0 || !server_.opened()) { + return; + } + + Socket client; + if (!server_.accept(&client)) { + return; + } + + const std::pair entry(registerClientId(), new ClientEntry(&binding_)); + const ClientMap::iterator iter = clients_.insert(entry).first; + iter->second->socket.set(client.release()); + iter->second->socket.setNagle(false); + iter->second->socket.setBlocking(false); +} + +void Server::advanceDisconnecting() { + std::vector clients; + for (ClientMap::iterator iter = clients_.begin(); iter != clients_.end(); ++iter) { + if (!iter->second->socket.connected()) { + clients.push_back(iter); + } + } + + for (std::vector::iterator iter = clients.begin(); iter != clients.end(); ++iter) { + disconnect((*iter)->first); + } +} + +void Server::advanceConnected() { + ASSERT(clientActive_ == CLIENT_ID_INVALID); + + for (ClientMap::iterator iter = clients_.begin(); iter != clients_.end(); ++iter) { + clientActive_ = iter->first; + iter->second->protocol.advance(); + } + + clientActive_ = CLIENT_ID_INVALID; +} + +ClientId Server::registerClientId() { + static int s_id = CLIENT_ID_INVALID; + while (++s_id == CLIENT_ID_INVALID); + return static_cast(s_id); +} + + +} diff --git a/mc_server.hpp b/mc_server.hpp new file mode 100644 index 0000000..27b9cb5 --- /dev/null +++ b/mc_server.hpp @@ -0,0 +1,101 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Types +// + +enum ClientId { + CLIENT_ID_INVALID +}; + +struct ClientData { + ClientData() : + id(CLIENT_ID_INVALID), + protocol(NULL) + { + } + + ClientId id; + std::string name; + Protocol* protocol; +}; + + +// +// Server +// + +class Server { +public: + Server(); + ~Server(); + + void advance (); + bool start (int serverPort, int clientsMax = 1); + void stop (); + void disconnect (ClientId id); + void disconnectAll (); + + void getClients (std::vector* clients) const; + bool getClient (ClientId id, ClientData* data) const; + ClientId clientActive () const; + int clientCount () const; + const Binding* binding () const; + Binding* binding (); + +private: + struct ClientEntry : public ClientData { + ClientEntry(Binding* binding) : + stream(&socket), + protocol(&stream, binding) + { + } + + Socket socket; + Stream stream; + Protocol protocol; + }; + + void advanceConnecting (); + void advanceConnected (); + void advanceDisconnecting (); + static ClientId registerClientId (); + + typedef std::map ClientMap; + ClientMap clients_; + int clientMax_; + ClientId clientActive_; + Socket server_; + Binding binding_; +}; + + +} diff --git a/mc_socket.cpp b/mc_socket.cpp new file mode 100644 index 0000000..4032e0b --- /dev/null +++ b/mc_socket.cpp @@ -0,0 +1,314 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_socket.hpp" + +namespace metacall { + + +// +// Constants +// + +namespace { + + +const int SOCKET_INVALID = -1; + + +} + + +// +// Socket +// + +Socket::Socket() : + socket_(SOCKET_INVALID) +{ +} + +Socket::~Socket() { + close(); +} + +bool Socket::open() { + close(); + socket_ = socket(AF_INET, SOCK_STREAM, 0); + return opened(); +} + +void Socket::close() { + if (!opened()) { + return; + } + +#ifdef _WIN32 + closesocket(socket_); +#else + ::close(socket_); +#endif + + socket_ = SOCKET_INVALID; +} + +int Socket::release() { + const int temp = socket_; + socket_ = SOCKET_INVALID; + return temp; +} + +void Socket::set(int socket) { + close(); + socket_ = socket; +} + +void Socket::setNagle(bool enable) { + ASSERT(opened()); + + const int value = enable ? 0 : 1; + const char* const valuePtr = reinterpret_cast(&value); + + setsockopt( + socket_, + IPPROTO_TCP, + TCP_NODELAY, + valuePtr, + sizeof(value) + ); +} + +void Socket::setBlocking(bool enable) { + ASSERT(opened()); + +#ifdef _WIN32 + unsigned long nonBlock = enable ? 0 : 1; + ioctlsocket(socket_, FIONBIO, &nonBlock); +#else + const int flagsOld = fcntl(socket_, F_GETFL); + const int flagsNew = enable ? flagsOld & ~O_NONBLOCK : flagsOld | O_NONBLOCK; + fcntl(socket_, F_SETFL, flagsNew); +#endif +} + +bool Socket::connect(const char name[], int port) { + ASSERT(opened()); + + long address = 0; + if (!resolve(name, &address)) { + return false; + } + + sockaddr_in host = { 0 }; + host.sin_port = htons(static_cast(port)); + host.sin_family = AF_INET; + host.sin_addr.s_addr = address; + + const sockaddr* const hostPtr = reinterpret_cast(&host); + if (::connect(socket_, hostPtr, sizeof(host)) == SOCKET_INVALID) { + return false; + } + + return true; +} + +bool Socket::bind(int port) { + ASSERT(opened()); + + sockaddr_in host = { 0 }; + host.sin_family = AF_INET; + host.sin_addr.s_addr = INADDR_ANY; + host.sin_port = htons(static_cast(port)); + + const sockaddr* const hostPtr = reinterpret_cast(&host); + if (::bind(socket_, hostPtr, sizeof(host)) == SOCKET_INVALID) { + return false; + } + + return true; +} + +bool Socket::listen(int backlog) { + ASSERT(opened()); + return ::listen(socket_, backlog) != SOCKET_INVALID; +} + +bool Socket::accept(Socket* socket) { + ASSERT(opened()); + + socket->close(); + socket->socket_ = ::accept(socket_, 0, 0); + + return socket->socket_ != SOCKET_INVALID; +} + +int Socket::receive(void* buffer, int size) { + ASSERT(opened()); + + if (size <= 0) { + return 0; + } + + return recv( + socket_, + static_cast(buffer), + size, + 0 + ); +} + +int Socket::peek(void* buffer, int size) const { + ASSERT(opened()); + + if (size <= 0) { + return 0; + } + + return recv( + socket_, + static_cast(buffer), + size, + MSG_PEEK + ); +} + +int Socket::send(const void* buffer, int size) { + ASSERT(opened()); + + if (size <= 0) { + return 0; + } + + return ::send( + socket_, + static_cast(buffer), + size, + 0 + ); +} + +bool Socket::wait(unsigned mask, int seconds) const { + ASSERT(opened()); + + fd_set readSet; + fd_set writeSet; + fd_set exceptSet; + + fd_set* readSetUse = NULL; + fd_set* writeSetUse = NULL; + fd_set* exceptSetUse = NULL; + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable:4127) +#endif + if (IS_TRUE(mask & MASK_READ)) { + readSetUse = &readSet; + FD_ZERO(readSetUse); + FD_SET(socket_, readSetUse); + } + if (IS_TRUE(mask & MASK_WRITE)) { + writeSetUse = &writeSet; + FD_ZERO(writeSetUse); + FD_SET(socket_, writeSetUse); + } + if (IS_TRUE(mask & MASK_EXCEPT)) { + exceptSetUse = &exceptSet; + FD_ZERO(exceptSetUse); + FD_SET(socket_, exceptSetUse); + } +#ifdef _WIN32 +#pragma warning(pop) +#endif + + timeval timeoutVal = { 0 }; + timeoutVal.tv_sec = seconds; + + timeval* const timeoutPtr = seconds < 0 ? NULL : &timeoutVal; + const int result = select( + socket_ + 1, + readSetUse, + writeSetUse, + exceptSetUse, + timeoutPtr + ); + + return result > 0; +} + +bool Socket::opened() const { + return socket_ != SOCKET_INVALID; +} + +bool Socket::connected() const { + if (!opened()) { + return false; + } + + if (!wait(MASK_READ, 0)) { + return true; + } + + byte buffer = 0; + if (peek(&buffer, sizeof(buffer)) == 0) { + return false; + } + + return true; +} + +const char* Socket::hostname() const { + if (!opened()) { + return NULL; + } + + sockaddr_in host = { 0 }; + socklen_t hostSize = sizeof(host); + sockaddr* const hostPtr = reinterpret_cast(&host); + + if (getsockname(socket_, hostPtr, &hostSize) == SOCKET_INVALID) { + return NULL; + } + + return inet_ntoa(host.sin_addr); +} + +bool Socket::resolve(const char name[], long* address) { + if ((*address = inet_addr(name)) != INADDR_NONE) { + return true; + } + + hostent* const host = gethostbyname(name); + if (host == NULL) { + return false; + } + + *address = *reinterpret_cast(host->h_addr_list[0]); + return true; +} + + +} diff --git a/mc_socket.hpp b/mc_socket.hpp new file mode 100644 index 0000000..f52d95e --- /dev/null +++ b/mc_socket.hpp @@ -0,0 +1,74 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Socket +// + +class Socket { +public: + enum { + MASK_READ = 1 << 0, + MASK_WRITE = 1 << 1, + MASK_EXCEPT = 1 << 2 + }; + + Socket(); + ~Socket(); + + bool open (); + void close (); + int release (); + void set (int socket); + void setNagle (bool enable); + void setBlocking (bool enable); + + bool connect (const char name[], int port); + bool bind (int port); + bool listen (int backlog = 5); + bool accept (Socket* socket); + + int receive (void* buffer, int size); + int peek (void* buffer, int size) const; + int send (const void* buffer, int size); + bool wait (unsigned mask, int seconds) const; + + bool opened () const; + bool connected () const; + const char* hostname () const; + +private: + static bool resolve (const char name[], long* address); + + int socket_; +}; + + +} diff --git a/mc_stream-inl.hpp b/mc_stream-inl.hpp new file mode 100644 index 0000000..c80acf4 --- /dev/null +++ b/mc_stream-inl.hpp @@ -0,0 +1,72 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace metacall { + +template +Stream::State Stream::send(const T& packet) { + if (!socket_->connected()) { + return STATE_ERROR_CONNECTION; + } + + Buffer buffTemp; + Serializer serializerTemp(&buffTemp); + serializerTemp.write(packet); + + PacketHeader header; + header.magic = PACKET_CONST_MAGIC; + header.size = serializerTemp.offset(); + header.id = T::Id; + + Serializer serializer_send(&buffSend_); + serializer_send.write(header); + serializer_send.writeRaw(buffTemp.data(), buffTemp.bytes()); + + return STATE_READY; +} + +template +Stream::State Stream::receive(T* packet) { + PacketHeader header; + int headerSize = 0; + + const State state = peek(&header, &headerSize); + if (state != STATE_READY) { + return state; + } + + buffRecv_.removeFromFront(NULL, headerSize); + + Deserializer deserializer(&buffRecv_); + const bool success = + deserializer.read(packet) && + deserializer.offset() == static_cast(header.size); + + buffRecv_.removeFromFront(NULL, header.size); + + return success ? STATE_READY : STATE_ERROR_PROTOCOL; +} + +} diff --git a/mc_stream.cpp b/mc_stream.cpp new file mode 100644 index 0000000..3230660 --- /dev/null +++ b/mc_stream.cpp @@ -0,0 +1,109 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_stream.hpp" + +namespace metacall { + + +// +// Stream +// + +Stream::Stream(Socket* socket) : + socket_(socket) +{ +} + +Stream::State Stream::advance() { + if (!socket_->connected()) { + return STATE_ERROR_CONNECTION; + } + + if (buffSend_.bytes() > 0 && socket_->wait(Socket::MASK_WRITE, 0)) { + const int bytesSent = socket_->send( + buffSend_.data(), + buffSend_.bytes() + ); + + if (bytesSent <= 0) { + return STATE_ERROR_CONNECTION; + } + + buffSend_.removeFromFront(NULL, bytesSent); + } + + if (socket_->wait(Socket::MASK_READ, 0)) { + const int bytesReceived = socket_->receive( + buffNet_.data(), + buffNet_.bytes() + ); + + if (bytesReceived <= 0) { + return STATE_ERROR_CONNECTION; + } + + buffRecv_.addToBack(buffNet_.data(), bytesReceived); + } + + return STATE_READY; +} + +void Stream::reset() { + buffSend_.clear(); + buffRecv_.clear(); +} + +Stream::State Stream::peek(PacketHeader* header, int* headerSize) { + Deserializer deserializer(&buffRecv_); + if (!deserializer.read(header)) { + return STATE_PENDING_PACKET_HEADER; + } + + if (header->magic != PACKET_CONST_MAGIC) { + return STATE_ERROR_PROTOCOL; + } + + *headerSize = deserializer.offset(); + + const int packetSize = static_cast(header->size) + *headerSize; + if (buffRecv_.bytes() < packetSize) { + return STATE_PENDING_PACKET_BODY; + } + + return STATE_READY; +} + +const Socket* Stream::socket() const { + return socket_; +} + +Socket* Stream::socket() { + return socket_; +} + + +} diff --git a/mc_stream.hpp b/mc_stream.hpp new file mode 100644 index 0000000..deff8ba --- /dev/null +++ b/mc_stream.hpp @@ -0,0 +1,67 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Stream +// + +class Stream { + public: + enum State { + STATE_READY, + STATE_PENDING_PACKET_HEADER, + STATE_PENDING_PACKET_BODY, + STATE_ERROR_PROTOCOL, + STATE_ERROR_CONNECTION + }; + + Stream(Socket* socket); + + State advance (); + void reset (); + + template + State send (const T& packet); + template + State receive (T* packet); + State peek (PacketHeader* header, int* headerSize); + + const Socket* socket () const; + Socket* socket (); + + private: + Socket* socket_; + Buffer buffRecv_; + Buffer buffSend_; + Buffer buffNet_; +}; + + +} diff --git a/mc_token.cpp b/mc_token.cpp new file mode 100644 index 0000000..aea7eef --- /dev/null +++ b/mc_token.cpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include "metacall.hpp" +#include "mc_token.hpp" + +namespace metacall { + +// +// Token +// + +Token::Token(const char value[]) : + value_(hash(value)) +{ +} + +Token::Token(unsigned value) : + value_(value) +{ +} + +Token::operator unsigned() const { + return value_; +} + +unsigned Token::hash(const char str[]) { + unsigned hash = 5381; + for (const char* ptr = str; *ptr != 0; ++ptr) { + hash = (hash << 5) + hash + *ptr; + } + return hash; +} + + +} diff --git a/mc_token.hpp b/mc_token.hpp new file mode 100644 index 0000000..276140e --- /dev/null +++ b/mc_token.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#pragma once + +namespace metacall { + + +// +// Token +// + +class Token { +public: + Token(const char value[]); + Token(unsigned value); + + operator unsigned() const; + +private: + static unsigned hash(const char str[]); + + unsigned value_; +}; + + +} diff --git a/metacall.hpp b/metacall.hpp new file mode 100644 index 0000000..d944a27 --- /dev/null +++ b/metacall.hpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2011 Alex Yatskov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#include +#include +#include +#include +#include + +#ifdef _WIN32 + #pragma comment(lib, "ws2_32.lib") + #include +#else + #include + #include + #include + #include + #include + #include +#endif + +#include "mc_common.hpp" +#include "mc_token.hpp" +#include "mc_buffer.hpp" +#include "mc_socket.hpp" +#include "mc_packet.hpp" +#include "mc_meta.hpp" +#include "mc_serial.hpp" +#include "mc_serial-inl.hpp" +#include "mc_functor.hpp" +#include "mc_binding.hpp" +#include "mc_binding-inl.hpp" +#include "mc_stream.hpp" +#include "mc_stream-inl.hpp" +#include "mc_protocol.hpp" +#include "mc_protocol-inl.hpp" +#include "mc_client.hpp" +#include "mc_server.hpp" diff --git a/metacall.project b/metacall.project new file mode 100644 index 0000000..202ab09 --- /dev/null +++ b/metacall.project @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + + + + + + + + + diff --git a/metacall.workspace b/metacall.workspace new file mode 100644 index 0000000..e07dc23 --- /dev/null +++ b/metacall.workspace @@ -0,0 +1,12 @@ + + + + + + + + + + + +