diff --git a/build.xml b/build.xml index 741b669ff..5118b5b50 100644 --- a/build.xml +++ b/build.xml @@ -18,7 +18,7 @@ - @@ -36,6 +36,7 @@ + @@ -163,7 +164,7 @@ - @@ -241,7 +242,7 @@ - + @@ -252,6 +253,7 @@ + @@ -283,6 +285,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + DisconnectEvent; +} \ No newline at end of file diff --git a/src/cpp/presents/InvocationDecoder.cpp b/src/cpp/presents/InvocationDecoder.cpp new file mode 100644 index 000000000..1249b4005 --- /dev/null +++ b/src/cpp/presents/InvocationDecoder.cpp @@ -0,0 +1,9 @@ +#include "stable.h" +#include "InvocationDecoder.h" + +using namespace presents; + +InvocationDecoder::InvocationDecoder(utf8 rcode) : + receiverCode(rcode) +{ +} \ No newline at end of file diff --git a/src/cpp/presents/InvocationDecoder.h b/src/cpp/presents/InvocationDecoder.h new file mode 100644 index 000000000..0cea2122f --- /dev/null +++ b/src/cpp/presents/InvocationDecoder.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Streamable.h" + +namespace presents +{ + class InvocationDecoder { + public: + const utf8 receiverCode; + InvocationDecoder(utf8 receiverCode); + virtual void dispatchNotification(int8 methodId, const std::vector< Shared >& args) = 0; + + private: + // not implemented + InvocationDecoder (const InvocationDecoder&); + InvocationDecoder& operator= (const InvocationDecoder&); + }; +} \ No newline at end of file diff --git a/src/cpp/presents/ObjectInputStream.cpp b/src/cpp/presents/ObjectInputStream.cpp new file mode 100644 index 000000000..ed429bde9 --- /dev/null +++ b/src/cpp/presents/ObjectInputStream.cpp @@ -0,0 +1,100 @@ +#include "stable.h" + +#include + +#include "ObjectInputStream.h" +#include "PresentsError.h" +#include "presents/io/InputStream.h" + +#include "Streamer.h" + +#include "Util.h" +bool ObjectInputStream::readBoolean () +{ + return readByte() != 0; +} + +namespace +{ + template + T readInteger(presents::InputStream* in) + { + T value; + assert(in->read(&value, sizeof(T)) == sizeof(T)); + return presents::presentsToHost(value); + } +} + +int8 ObjectInputStream::readByte () +{ + return readInteger(_in); +} + +int16 ObjectInputStream::readShort () +{ + return readInteger(_in); +} + +int32 ObjectInputStream::readInt () +{ + return readInteger(_in); +} + +int64 ObjectInputStream::readLong () +{ + return readInteger(_in); +} + +void ObjectInputStream::readBytes (uint8* bytes, size_t length) +{ + _in->read(bytes, length); +} + +float ObjectInputStream::readFloat () +{ + float value; + readBytes((uint8*) &value, 4); + return value; +} + +double ObjectInputStream::readDouble () +{ + double value; + readBytes((uint8*) &value, 8); + return value; +} + +utf8 ObjectInputStream::readUTF () +{ + uint16 dataLength = readInteger(_in); + boost::scoped_array data(new uint8[dataLength]); + readBytes((uint8*)&data[0], dataLength); + return std::string((char*)&data[0], dataLength); +} + +Shared ObjectInputStream::readObject () +{ + int16 code = readShort(); + if (code == 0) { + return Shared(); + } + + Streamer* streamer; + if (code < 0) { + code *= -1; + + utf8 name = readUTF(); + + streamer = getStreamer(name); + ClassMapping cmap = { code, streamer }; + _classMap.insert(_classMap.begin() + code, cmap); + + } else { + streamer = (&_classMap[code])->streamer; + if (streamer == NULL) { + throw PresentsError((boost::format("Unknown streamer code '%1%'") % code).str()); + } + } + + return streamer->createObject(*this); +} \ No newline at end of file diff --git a/src/cpp/presents/ObjectInputStream.h b/src/cpp/presents/ObjectInputStream.h new file mode 100644 index 000000000..322507004 --- /dev/null +++ b/src/cpp/presents/ObjectInputStream.h @@ -0,0 +1,49 @@ +// +// $Id$ + +#pragma once + +#include "ClassMapping.h" +#include "Streamer.h" + +namespace presents { class InputStream; } + +using namespace presents; + +class ObjectInputStream +{ +public: + ObjectInputStream (presents::InputStream* stream) : _in(stream), _classMap(1) { } + + bool readBoolean (); + int8 readByte (); + int16 readShort (); + int32 readInt (); + int64 readLong (); + void readBytes (uint8* bytes, size_t length); + float readFloat (); + double readDouble (); + utf8 readUTF (); + + Shared readObject (); + + template + Shared readField () + { + return boost::static_pointer_cast(readField(getJavaName((const T*)NULL))); + } + + Shared readField (utf8 javaname) + { + if (readBoolean()) { + return getStreamer(javaname)->createObject(*this); + } else { + return Shared(); + } + } + +protected: + presents::InputStream* _in; + typedef std::vector ClassMap; + ClassMap _classMap; +}; diff --git a/src/cpp/presents/ObjectOutputStream.cpp b/src/cpp/presents/ObjectOutputStream.cpp new file mode 100644 index 000000000..10f58fae4 --- /dev/null +++ b/src/cpp/presents/ObjectOutputStream.cpp @@ -0,0 +1,81 @@ +#include "stable.h" + +#include "presents/io/OutputStream.h" +#include "ObjectOutputStream.h" + +#include "Streamable.h" +#include "Streamer.h" +#include "Util.h" + +#pragma warning (disable: 4100) // unreferenced parameter + +using namespace presents; + +namespace { +template +static void writeInteger(OutputStream* out, T value) +{ + value = hostToPresents(value); + out->write((uint8*)&value, sizeof(value)); +} +} + +void ObjectOutputStream::writeBoolean (bool value) +{ + writeInteger(_out, (uint8)(value ? 1 : 0)); +} + +void ObjectOutputStream::writeByte (int8 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeLong (int64 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeInt (int32 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeShort (int16 value) +{ + writeInteger(_out, value); +} + +void ObjectOutputStream::writeBytes (const uint8* bytes, size_t length) +{ + _out->write(bytes, length); +} + +void ObjectOutputStream::writeDouble (double value) +{ + _out->write((uint8*) &value, 8); +} + +void ObjectOutputStream::writeFloat (float value) +{ + _out->write((uint8*) &value, 4); +} + +void ObjectOutputStream::writeUTF (const utf8& value) +{ + size_t bytesToWrite = value.size() * sizeof(uint8); + writeShort(bytesToWrite); + _out->write((uint8*)value.data(), value.size()); +} + +void ObjectOutputStream::writeBareObject (const Shared& object, Streamer* streamer) +{ + streamer->writeObject(object, *this); +} + +void ObjectOutputStream::writeField (const Shared& object, const utf8& javaName) +{ + writeBoolean(object != NULL); + if (object != NULL) { + writeBareObject(object, getStreamer(javaName)); + } +} diff --git a/src/cpp/presents/ObjectOutputStream.h b/src/cpp/presents/ObjectOutputStream.h new file mode 100644 index 000000000..50d2da487 --- /dev/null +++ b/src/cpp/presents/ObjectOutputStream.h @@ -0,0 +1,89 @@ +// +// $Id$ + +#pragma once + +#include +#include +#include +#include + +#include "ClassMapping.h" +#include "PresentsError.h" +#include "Streamable.h" +#include "Streamer.h" + +//interface Streamable; +//interface Streamer; + +namespace presents { class OutputStream; } + +using namespace presents; + +class ObjectOutputStream +{ +public: + ObjectOutputStream (presents::OutputStream* stream) : _out(stream), _nextClassCode(1) { } + + void writeBoolean (bool value); + void writeByte (int8 value); + void writeShort (int16 value); + void writeInt (int32 value); + void writeLong (int64 value); + void writeBytes (const uint8* bytes, size_t length); + void writeDouble (double value); + void writeFloat (float value); + void writeUTF (const utf8& value); + + template + void writeObject (const Shared& object) + { + if (object.get() == NULL) { + writeShort(0); + return; + } + + const utf8 className = getObjectName(object.get()); + ClassMap::iterator it = _classMap.find(className); + Streamer* streamer; + + if (it == _classMap.end()) { + streamer = getStreamer(className); + if (streamer == NULL) { + throw PresentsError((boost::format("No streamer for '%1%'") % className).str()); + } + + ClassMapping cmap = { (int16)_nextClassCode++, streamer }; + assert(_nextClassCode < 32767); // Can't be more than Short.MAX_VALUE + _classMap[className] = cmap; + + writeShort(-cmap.code); + writeUTF(className); + + } else { + ClassMapping* cmap = &(it->second); + streamer = cmap->streamer; + writeShort(cmap->code); + } + + writeBareObject((Shared)object, streamer); + } + + template + void writeField (const Shared& object) + { + writeField(object, getJavaName((T*)NULL)); + } + + void writeField (const Shared& object, const utf8& javaName); + +protected: + void writeBareObject (const Shared& object, Streamer* streamer); + + presents::OutputStream* _out; + + typedef std::map ClassMap; + ClassMap _classMap; + + int _nextClassCode; +}; diff --git a/src/cpp/presents/PresentsClient.cpp b/src/cpp/presents/PresentsClient.cpp new file mode 100644 index 000000000..4c00e7bc6 --- /dev/null +++ b/src/cpp/presents/PresentsClient.cpp @@ -0,0 +1,252 @@ +// +// $Id$ + +#include "presents/stable.h" +#include "PresentsClient.h" + +#include "SocketStream.h" +#include "ObjectInputStream.h" +#include "ObjectOutputStream.h" + +#include "presents/box/BoxedBoolean.h" +#include "presents/box/BoxedByte.h" +#include "presents/box/BoxedDouble.h" +#include "presents/box/BoxedFloat.h" +#include "presents/box/BoxedInt.h" +#include "presents/box/BoxedShort.h" + +#include "presents/client/Registration.h" + +#include "presents/data/ClientObject.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/data/PermissionPolicy.h" +#include "presents/data/RegistrationMarshaller.h" + +#include "presents/dobj/DEvent.h" +#include "presents/dobj/DObject.h" +#include "presents/dobj/DSet.h" +#include "presents/dobj/EntryAddedEvent.h" +#include "presents/dobj/EntryRemovedEvent.h" +#include "presents/dobj/InvocationNotificationEvent.h" +#include "presents/dobj/InvocationRequestEvent.h" +#include "presents/dobj/NamedEvent.h" + +#include "presents/io/FramedInputStream.h" +#include "presents/io/FramingOutputStream.h" + +#include "presents/net/AuthRequest.h" +#include "presents/net/AuthResponse.h" +#include "presents/net/AuthResponseData.h" +#include "presents/net/BootstrapData.h" +#include "presents/net/BootstrapNotification.h" +#include "presents/net/Credentials.h" +#include "presents/net/DownstreamMessage.h" +#include "presents/net/EventNotification.h" +#include "presents/net/ForwardEventRequest.h" +#include "presents/net/ObjectResponse.h" +#include "presents/net/PingRequest.h" +#include "presents/net/PongResponse.h" +#include "presents/net/UpstreamMessage.h" +#include "presents/net/UsernamePasswordCreds.h" + +#include "util/Name.h" + +#include "presents/Util.h" + +using namespace presents; + +PresentsClient::PresentsClient () + : _outMessageId(0) + , _nextReceiverId(1) + , _oid(-1) + , _socket(NULL) + , _idleTime(0) + , _connected(false) + , _active(false) +{ + // ensure all Streamables are not stripped out of the library by clients + // (Some of these classes aren't directly referenced by PresentsClient) + box::BoxedBoolean::registerWithPresents(); + box::BoxedByte::registerWithPresents(); + box::BoxedDouble::registerWithPresents(); + box::BoxedFloat::registerWithPresents(); + box::BoxedInt::registerWithPresents(); + box::BoxedShort::registerWithPresents(); + + client::Registration::registerWithPresents(); + + data::ClientObject::registerWithPresents(); + data::InvocationMarshaller::registerWithPresents(); + data::PermissionPolicy::registerWithPresents(); + data::RegistrationMarshaller::registerWithPresents(); + + dobj::DEvent::registerWithPresents(); + dobj::DObject::registerWithPresents(); + dobj::DSet::registerWithPresents(); + dobj::EntryAddedEvent::registerWithPresents(); + dobj::EntryRemovedEvent::registerWithPresents(); + dobj::InvocationNotificationEvent::registerWithPresents(); + dobj::InvocationRequestEvent::registerWithPresents(); + dobj::NamedEvent::registerWithPresents(); + + net::AuthRequest::registerWithPresents(); + net::AuthResponse::registerWithPresents(); + net::AuthResponseData::registerWithPresents(); + net::BootstrapData::registerWithPresents(); + net::BootstrapNotification::registerWithPresents(); + net::Credentials::registerWithPresents(); + net::DownstreamMessage::registerWithPresents(); + net::EventNotification::registerWithPresents(); + net::ForwardEventRequest::registerWithPresents(); + net::ObjectResponse::registerWithPresents(); + net::PingRequest::registerWithPresents(); + net::PongResponse::registerWithPresents(); + net::UpstreamMessage::registerWithPresents(); + net::UsernamePasswordCreds::registerWithPresents(); +} + +PresentsClient::~PresentsClient () +{ +} + +bool PresentsClient::isConnected () const +{ + return _connected; +} + +void PresentsClient::connect (const std::string& host, int port, Shared creds, Shared version) +{ + assert(!_active); + assert(!_onSocketDisconnect.connected()); + _active = true; + _socket.reset(createSocketStream(host.c_str(), port)); + _onSocketDisconnect = _socket->onDisconnect(boost::bind(&PresentsClient::handleDisconnect, this, _1)); + _framedIn.reset(new FramedInputStream(_socket.get())); + _framingOut.reset(new FramingOutputStream(_socket.get())); + _in.reset(new ObjectInputStream(_framedIn.get())); + _out.reset(new ObjectOutputStream(_framingOut.get())); + + Shared req(new net::AuthRequest); + req->version = version; + req->zone = Shared(new utf8("")); + + req->creds = creds; + req->bootGroups = Shared< std::vector< Shared > >(new std::vector< Shared >); + req->bootGroups->push_back(Shared(new utf8("client"))); + PLOG("Connecting to %s:%d", host.c_str(), port); + postMessage(req); +} + +void PresentsClient::disconnect () +{ + if (_active) { + _socket->close(); + } +} + +void PresentsClient::update (float dt) +{ + if (!_active) { + return; + } + _socket->update(dt); + if (!_active) { + // Update can inform of uf a disconnect through onDisconnect, which shuts us down. + return; + } + while (_framedIn->readFrame()) { + Shared obj = boost::static_pointer_cast(_in->readObject()); + if (obj == NULL) { + PLOG("Got null message? What the dickens?"); + continue; + } + const utf8& msgName = obj->getJavaClassName(); + if (msgName == net::AuthResponse::javaName()) { + Shared resp = boost::static_pointer_cast(obj); + PLOG("Got auth response from corpseserver: %s", resp->data->code->c_str()); + _onAuth(*resp->data); + } else if (msgName == net::BootstrapNotification::javaName()) { + PLOG("Got bootstrap"); + Shared strap = boost::static_pointer_cast(obj); + _oid = strap->data->clientOid; + typedef std::vector< Shared > MarshVec; + Shared services(strap->data->services); + for (MarshVec::iterator iter = services->begin(); iter != services->end(); ++iter) { + _services.insert(MarshallerMap::value_type((*iter)->getJavaClassName(), *iter)); + } + _connected = true; + _onConnected(this); + } else if (msgName == net::EventNotification::javaName()) { + Shared event = boost::static_pointer_cast(obj)->event; + if (event->getJavaClassName() == dobj::InvocationNotificationEvent::javaName()) { + Shared notification = boost::static_pointer_cast(event); + DecoderMap::iterator it = _decoders.find(notification->receiverId); + it->second->dispatchNotification(notification->methodId, *notification->args); + } else { + PLOG("Ignoring event %s", event->getJavaClassName().c_str()); + } + } else { + PLOG("Ignoring message %s", msgName.c_str()); + } + if (!_active) { + // The client may decide to disconnect as a result of reading the message, in which case we need to stop reading. + return; + } + } + _idleTime += dt; + if (!_connected) { + if (_idleTime > 30) { + handleDisconnect(SERVER_UNREACHABLE); + } + } else if (_idleTime > 60) { + Shared ping(new net::PingRequest()); + postMessage(ping); + } +} + +void PresentsClient::postMessage (Shared message) +{ + if (!_active) { + PLOG("PresentsClient::postMessage called while not connected!"); + } else { + message->messageId = _outMessageId++; + _out->writeObject(message); + _framingOut->writeFrame(); + _idleTime = 0; + } +} + +void PresentsClient::sendRequest (int32 invOid, int32 invCode, int8 methId, const Shared< std::vector< Shared > >& args) +{ + Shared req(new dobj::InvocationRequestEvent); + req->toid = invOid; + req->invCode = invCode; + req->methodId = methId; + req->args = args; + Shared forward(new net::ForwardEventRequest); + forward->event = req; + postMessage(forward); +} + + +void PresentsClient::handleDisconnect (DisconnectReason reason) +{ + PLOG("PresentsClient::handleDisconnect"); + _active = false; + _socket.reset(); + _in.reset(); + _out.reset(); + _framedIn.reset(); + _framingOut.reset(); + _oid = -1; + _decoders.clear(); + _services.clear(); + _onSocketDisconnect.disconnect(); + _connected = false; + _onDisconnected(reason); +} + +Shared PresentsClient::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/cpp/presents/PresentsClient.h b/src/cpp/presents/PresentsClient.h new file mode 100644 index 000000000..421b8a591 --- /dev/null +++ b/src/cpp/presents/PresentsClient.h @@ -0,0 +1,116 @@ +// +// $Id$ + +#pragma once + +#include + +#include "presents/InvocationDecoder.h" +#include "presents/DisconnectReason.h" +#include "presents/PresentsError.h" +#include "presents/client/Registration.h" +#include "presents/data/RegistrationMarshaller.h" + +class ObjectInputStream; +class ObjectOutputStream; + +namespace presents +{ +namespace data { class InvocationMarshaller; } +namespace net { + class AuthResponseData; + class Credentials; + class UpstreamMessage; +} +class FramedInputStream; +class FramingOutputStream; +class SocketStream; + +class PresentsClient : public boost::enable_shared_from_this +{ +protected: + typedef std::map > MarshallerMap; + typedef std::map > DecoderMap; + +public: + typedef boost::signal ClientEvent; + typedef boost::signal AuthFailureEvent; + + PresentsClient (); + virtual ~PresentsClient (); + + void update (float dt); + + template + Shared requireService () + { + MarshallerMap::iterator it = _services.find(T::javaName()); + if (it->second == NULL) { + throw PresentsError((boost::format("No marshaller for %1%") % T::javaName()).str()); + } + return boost::static_pointer_cast(it->second); + } + + template + void registerReceiver (Shared receiver) + { + assert(_socket != NULL); + Shared reg(new presents::client::Registration); + reg->receiverId = _nextReceiverId++; + Shared dec(new typename T::Decoder(receiver)); + reg->receiverCode = Shared(new utf8(dec->receiverCode)); + _decoders.insert(DecoderMap::value_type(reg->receiverId, dec)); + requireService()->registerReceiver(getSharedThis(), reg); + } + + bool isConnected () const; + void connect (const std::string& hostname, int port, Shared creds, Shared version); + void disconnect (); + + void postMessage (Shared msg); + void sendRequest (int32 invOid, int32 invCode, int8 methodId, const Shared< std::vector< Shared > >& args); + int getOid() const { assert(_oid != -1); return _oid; } + + connection_t onConnected (const ClientEvent::slot_type& slot) { + return _onConnected.connect(slot); + } + + connection_t onDisconnected (const DisconnectEvent::slot_type& slot) { + return _onDisconnected.connect(slot); + } + + connection_t onAuth (const AuthFailureEvent::slot_type& slot) { + return _onAuth.connect(slot); + } + +protected: + void handleDisconnect (DisconnectReason); + Shared getSharedThis(); + + boost::scoped_ptr _socket; + boost::scoped_ptr _framingOut; + boost::scoped_ptr _framedIn; + boost::scoped_ptr _in; + boost::scoped_ptr _out; + int16 _outMessageId; + int16 _nextReceiverId; + int32 _oid; + DecoderMap _decoders; + MarshallerMap _services; + float _idleTime; + + // If the connection to the presents server is authenticated and ready to go. _onConnected is + // signaled when this becomes true + bool _connected; + + // If we're connected or are trying to connect. This is always true when _connected is true, + // but also before the connection is authed and before the bootstrap data arrives. + bool _active; + + ClientEvent _onConnected; + DisconnectEvent _onDisconnected; + AuthFailureEvent _onAuth; + + connection_t _onSocketDisconnect; +}; +} diff --git a/src/cpp/presents/PresentsError.h b/src/cpp/presents/PresentsError.h new file mode 100644 index 000000000..5fd8ec952 --- /dev/null +++ b/src/cpp/presents/PresentsError.h @@ -0,0 +1,14 @@ +#pragma once + +namespace presents +{ + class PresentsError : public std::runtime_error + { + public: + PresentsError(const std::string& message) : std::runtime_error(message) + {} + + virtual ~PresentsError() throw() + {} + }; +} \ No newline at end of file diff --git a/src/cpp/presents/SocketStream.h b/src/cpp/presents/SocketStream.h new file mode 100644 index 000000000..af6033143 --- /dev/null +++ b/src/cpp/presents/SocketStream.h @@ -0,0 +1,18 @@ +#pragma once + +#include "DisconnectReason.h" +#include "io/InputStream.h" +#include "io/OutputStream.h" + +namespace presents { +class SocketStream : public InputStream, public OutputStream +{ +public: + virtual ~SocketStream() {}; + virtual void close () = 0; + virtual void update (float dt) = 0; + virtual connection_t onDisconnect (const DisconnectEvent::slot_type& slot) = 0; +}; + +SocketStream* createSocketStream (const char* hostname, int port); +} \ No newline at end of file diff --git a/src/cpp/presents/Streamable.h b/src/cpp/presents/Streamable.h new file mode 100644 index 000000000..d0f69e0d2 --- /dev/null +++ b/src/cpp/presents/Streamable.h @@ -0,0 +1,15 @@ +// +// $Id$ + +#pragma once + +class ObjectOutputStream; +class ObjectInputStream; + +struct Streamable +{ + virtual void writeObject (ObjectOutputStream& out) const = 0; + virtual void readObject (ObjectInputStream& in) = 0; + virtual const utf8& getJavaClassName () const = 0; +}; + diff --git a/src/cpp/presents/Streamer.cpp b/src/cpp/presents/Streamer.cpp new file mode 100644 index 000000000..48d2e3241 --- /dev/null +++ b/src/cpp/presents/Streamer.cpp @@ -0,0 +1,98 @@ +#include "stable.h" +#include "Streamer.h" + +#include +#include +#include + +#include "ObjectInputStream.h" +#include "ObjectOutputStream.h" +#include "PresentsError.h" + +#include "streamers/StringStreamer.h" +#include "streamers/StreamableStreamer.h" +#include "streamers/VectorStreamer.h" + +#include "Util.h" + +class StreamerMap +{ +private: + typedef std::map Map; + +public: + void insert (const utf8& javaName, Streamer* streamer) + { + _map[javaName] = streamer; + } + + Streamer* get (const utf8& javaName) + { + Map::iterator it = _map.find(javaName); + if (it == _map.end()) { + throw PresentsError((boost::format("Unable to find Streamer for %1%") % javaName).str()); + } + return it->second; + } + + ~StreamerMap () + { + for (Map::iterator iter = _map.begin(); iter != _map.end(); ++iter) { + delete iter->second; + } + } + +private: + Map _map; +}; + +namespace +{ + template + void registerPrimitiveArrayStreamer (StreamerMap& map) + { + map.insert(getJavaName((const std::vector*)NULL), new VectorStreamer()); + } + + template + void registerObjectArrayStreamer (StreamerMap& map) + { + map.insert(getJavaName<>((std::vector< Shared >*)NULL), new VectorStreamer()); + } +} + +StreamerMap* getMap () +{ + static bool gInited = false; + static StreamerMap gMap; + + if (!gInited) { + gInited = true; + + gMap.insert(getJavaName((utf8*)NULL), new StringStreamer()); + gMap.insert(JAVA_LIST_NAME(), new VectorStreamer< Streamable >()); + + registerObjectArrayStreamer(gMap); + registerObjectArrayStreamer(gMap); + + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + registerPrimitiveArrayStreamer(gMap); + } + + return &gMap; +} + +void registerStreamer (const utf8& javaName, Streamer* streamer) +{ + getMap()->insert(javaName, streamer); +} + +Streamer* getStreamer (const utf8& javaName) +{ + return getMap()->get(javaName); +} diff --git a/src/cpp/presents/Streamer.h b/src/cpp/presents/Streamer.h new file mode 100644 index 000000000..5eb5a3fa0 --- /dev/null +++ b/src/cpp/presents/Streamer.h @@ -0,0 +1,103 @@ +// +// $Id$ + +#pragma once + +#include +#include "Streamable.h" + +static inline utf8 JAVA_LIST_NAME () { return "java.util.ArrayList"; } + +class ObjectOutputStream; +class ObjectInputStream; + +struct Streamer +{ + virtual Shared createObject (ObjectInputStream& in) = 0; + virtual void writeObject (const Shared& object, ObjectOutputStream& out) = 0; +}; + +Streamer* getStreamer (const utf8& javaName); +void registerStreamer (const utf8& javaName, Streamer* streamer); + +inline const utf8 getJavaName (const utf8*) +{ + return "java.lang.String"; +} + +inline const utf8 getJavaName (const int8*) +{ + return "B"; +} + +inline const utf8 getJavaName (const int16*) +{ + return "S"; +} + +inline const utf8 getJavaName (const int32*) +{ + return "I"; +} + +inline const utf8 getJavaName (const int64*) +{ + return "J"; +} + +inline const utf8 getJavaName (const bool*) +{ + return "Z"; +} + +inline const utf8 getJavaName (const float*) +{ + return "F"; +} + +inline const utf8 getJavaName (const double*) +{ + return "D"; +} + +template +inline const utf8 getJavaName (const Shared*) +{ + return getJavaName((const T*)NULL); +} + +template +inline const utf8 getJavaName (const std::vector*) +{ + return "[" + getJavaName((const T*)NULL); +} + +template +inline const utf8 getJavaName (const std::vector< Shared >*) +{ + return "[L" + getJavaName((const T*)NULL) + ";"; +} + +template +inline const utf8 getJavaName (const T*) +{ + return T::javaName(); +} + +template <> +inline const utf8 getJavaName (const Streamable*) +{ + return "java.lang.Object"; +} + +template +inline const utf8 getObjectName (T* object) +{ + if (boost::is_base_of::value) { + // Look up the object's class virtually + return ((Streamable*)object)->getJavaClassName(); + } else { + // Everything else is assumed to be a final class + return getJavaName(object); + } +} diff --git a/src/cpp/presents/Util.cpp b/src/cpp/presents/Util.cpp new file mode 100644 index 000000000..185a5fd36 --- /dev/null +++ b/src/cpp/presents/Util.cpp @@ -0,0 +1,18 @@ +#include "Util.h" + +void presents::internal::log(const char* format, ...) +{ + va_list argumentList; + va_start(argumentList, format); + + char buffer[1024]; +#ifdef PRESENTS_COMPILER_MSVC + if (_vsnprintf(buffer, 1024, format, argumentList) > 0) { +#else + if (vsprintf(buffer, format, argumentList) > 0) { +#endif + presents::log(buffer); + } + + va_end(argumentList); +} \ No newline at end of file diff --git a/src/cpp/presents/Util.h b/src/cpp/presents/Util.h new file mode 100644 index 000000000..1883da2a1 --- /dev/null +++ b/src/cpp/presents/Util.h @@ -0,0 +1,37 @@ +#pragma once + +namespace presents { + void log (const char* message); + + namespace internal { + void log (const char* format, ...); + } + + template + inline T reverseBytes (T source) + { + uint8* pData = reinterpret_cast(&source); + std::reverse(pData, pData + sizeof(T)); + return source; + } + + template + T presentsToHost (T source) + { +#ifdef PRESENTS_HOST_LITTLE_ENDIAN + return reverseBytes(source); +#else + return source; +#endif + } + + template + T hostToPresents (T source) + { +#ifdef PRESENTS_HOST_LITTLE_ENDIAN + return reverseBytes(source); +#else + return source; +#endif + } +} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedBoolean.cpp b/src/cpp/presents/box/BoxedBoolean.cpp new file mode 100644 index 000000000..a1b72e926 --- /dev/null +++ b/src/cpp/presents/box/BoxedBoolean.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedBoolean.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Boolean", BoxedBoolean); + +void BoxedBoolean::readObject (ObjectInputStream& in) +{ + value = in.readBoolean(); +} + +void BoxedBoolean::writeObject (ObjectOutputStream& out) const +{ + out.writeBoolean(value); +} diff --git a/src/cpp/presents/box/BoxedBoolean.h b/src/cpp/presents/box/BoxedBoolean.h new file mode 100644 index 000000000..edc689f8a --- /dev/null +++ b/src/cpp/presents/box/BoxedBoolean.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedBoolean : public Streamable{ + public: + DECLARE_STREAMABLE(); + + bool value; + + static Shared createShared (bool value) + { + Shared shared(new BoxedBoolean); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedByte.cpp b/src/cpp/presents/box/BoxedByte.cpp new file mode 100644 index 000000000..ffd43060d --- /dev/null +++ b/src/cpp/presents/box/BoxedByte.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedByte.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Byte", BoxedByte); + +void BoxedByte::readObject (ObjectInputStream& in) +{ + value = in.readByte(); +} + +void BoxedByte::writeObject (ObjectOutputStream& out) const +{ + out.writeByte(value); +} diff --git a/src/cpp/presents/box/BoxedByte.h b/src/cpp/presents/box/BoxedByte.h new file mode 100644 index 000000000..53546a2f6 --- /dev/null +++ b/src/cpp/presents/box/BoxedByte.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedByte : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int8 value; + + static Shared createShared (int8 value) + { + Shared shared(new BoxedByte); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedDouble.cpp b/src/cpp/presents/box/BoxedDouble.cpp new file mode 100644 index 000000000..0fa214ee4 --- /dev/null +++ b/src/cpp/presents/box/BoxedDouble.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedDouble.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Double", BoxedDouble); + +void BoxedDouble::readObject (ObjectInputStream& in) +{ + value = in.readDouble(); +} + +void BoxedDouble::writeObject (ObjectOutputStream& out) const +{ + out.writeDouble(value); +} diff --git a/src/cpp/presents/box/BoxedDouble.h b/src/cpp/presents/box/BoxedDouble.h new file mode 100644 index 000000000..98224a564 --- /dev/null +++ b/src/cpp/presents/box/BoxedDouble.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedDouble : public Streamable{ + public: + DECLARE_STREAMABLE(); + + double value; + + static Shared createShared (double value) + { + Shared shared(new BoxedDouble); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedFloat.cpp b/src/cpp/presents/box/BoxedFloat.cpp new file mode 100644 index 000000000..41e45979e --- /dev/null +++ b/src/cpp/presents/box/BoxedFloat.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedFloat.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Float", BoxedFloat); + +void BoxedFloat::readObject (ObjectInputStream& in) +{ + value = in.readFloat(); +} + +void BoxedFloat::writeObject (ObjectOutputStream& out) const +{ + out.writeFloat(value); +} diff --git a/src/cpp/presents/box/BoxedFloat.h b/src/cpp/presents/box/BoxedFloat.h new file mode 100644 index 000000000..c0aaa6222 --- /dev/null +++ b/src/cpp/presents/box/BoxedFloat.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedFloat : public Streamable{ + public: + DECLARE_STREAMABLE(); + + float value; + + static Shared createShared (float value) + { + Shared shared(new BoxedFloat); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedInt.cpp b/src/cpp/presents/box/BoxedInt.cpp new file mode 100644 index 000000000..8e9707a34 --- /dev/null +++ b/src/cpp/presents/box/BoxedInt.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedInt.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Integer", BoxedInt); + +void BoxedInt::readObject (ObjectInputStream& in) +{ + value = in.readInt(); +} + +void BoxedInt::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(value); +} diff --git a/src/cpp/presents/box/BoxedInt.h b/src/cpp/presents/box/BoxedInt.h new file mode 100644 index 000000000..7bbdd6933 --- /dev/null +++ b/src/cpp/presents/box/BoxedInt.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedInt : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int32 value; + + static Shared createShared (int32 value) + { + Shared shared(new BoxedInt); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/BoxedShort.cpp b/src/cpp/presents/box/BoxedShort.cpp new file mode 100644 index 000000000..a63a44164 --- /dev/null +++ b/src/cpp/presents/box/BoxedShort.cpp @@ -0,0 +1,19 @@ +#include "presents/stable.h" +#include "BoxedShort.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.Short", BoxedShort); + +void BoxedShort::readObject (ObjectInputStream& in) +{ + value = in.readShort(); +} + +void BoxedShort::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(value); +} diff --git a/src/cpp/presents/box/BoxedShort.h b/src/cpp/presents/box/BoxedShort.h new file mode 100644 index 000000000..b18c44c30 --- /dev/null +++ b/src/cpp/presents/box/BoxedShort.h @@ -0,0 +1,24 @@ +#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class BoxedShort : public Streamable{ + public: + DECLARE_STREAMABLE(); + + int16 value; + + static Shared createShared (int16 value) + { + Shared shared(new BoxedShort); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}} \ No newline at end of file diff --git a/src/cpp/presents/box/boxer.py b/src/cpp/presents/box/boxer.py new file mode 100644 index 000000000..0604dfd8e --- /dev/null +++ b/src/cpp/presents/box/boxer.py @@ -0,0 +1,55 @@ +primitives = [("Int", "int32", "Integer"), + ("Short", "int16", "Short"), + ("Byte", "int8", "Byte"), + ("Boolean", "bool", "Boolean"), + ("Double", "double", "Double"), + ("Float", "float", "Float")] +for interpreter, cpptype, javatype in primitives: + header = open("Boxed" + interpreter + ".h", 'w') + header.write('''#pragma once +// Generated by running python boxer.py in this directory + +#include "presents/Streamable.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace box { + class Boxed%(interpreter)s : public Streamable{ + public: + DECLARE_STREAMABLE(); + + %(cpptype)s value; + + static Shared createShared (%(cpptype)s value) + { + Shared shared(new Boxed%(interpreter)s); + shared->value = value; + return shared; + } + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; + }; +}}''' % locals()) + header.close() + impl = open("Boxed" + interpreter + ".cpp", "w") + impl.write('''#include "presents/stable.h" +#include "Boxed%(interpreter)s.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +// Generated by running python boxer.py in this directory + +using namespace presents::box; + +DEFINE_STREAMABLE("java.lang.%(javatype)s", Boxed%(interpreter)s); + +void Boxed%(interpreter)s::readObject (ObjectInputStream& in) +{ + value = in.read%(interpreter)s(); +} + +void Boxed%(interpreter)s::writeObject (ObjectOutputStream& out) const +{ + out.write%(interpreter)s(value); +} +''' % locals()) + impl.close() diff --git a/src/cpp/presents/client/Registration.cpp b/src/cpp/presents/client/Registration.cpp new file mode 100644 index 000000000..072f9d214 --- /dev/null +++ b/src/cpp/presents/client/Registration.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "Registration.h" + +using namespace presents::client; + +DEFINE_STREAMABLE("com.threerings.presents.client.InvocationReceiver$Registration", Registration); + +void Registration::readObject (ObjectInputStream& in) +{ + receiverCode = in.readField< utf8 >(); + receiverId = int16(in.readShort()); +} + +void Registration::writeObject (ObjectOutputStream& out) const +{ + out.writeField(receiverCode); + out.writeShort(receiverId); +} diff --git a/src/cpp/presents/client/Registration.h b/src/cpp/presents/client/Registration.h new file mode 100644 index 000000000..b60c47a89 --- /dev/null +++ b/src/cpp/presents/client/Registration.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace client { + +class Registration : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared receiverCode; + int16 receiverId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/ClientObject.cpp b/src/cpp/presents/data/ClientObject.cpp new file mode 100644 index 000000000..f083e5585 --- /dev/null +++ b/src/cpp/presents/data/ClientObject.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "ClientObject.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.ClientObject", ClientObject); + +void ClientObject::readObject (ObjectInputStream& in) +{ + presents::dobj::DObject::readObject(in); + username = boost::static_pointer_cast(in.readObject()); + receivers = boost::static_pointer_cast(in.readObject()); + permPolicy = boost::static_pointer_cast(in.readObject()); +} + +void ClientObject::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DObject::writeObject(out); + out.writeObject(username); + out.writeObject(receivers); + out.writeObject(permPolicy); +} diff --git a/src/cpp/presents/data/ClientObject.h b/src/cpp/presents/data/ClientObject.h new file mode 100644 index 000000000..8448b193b --- /dev/null +++ b/src/cpp/presents/data/ClientObject.h @@ -0,0 +1,27 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DObject.h" +#include "util/Name.h" +#include "presents/data/PermissionPolicy.h" +#include "presents/dobj/DSet.h" + +namespace presents { namespace data { + +class ClientObject : public presents::dobj::DObject { +public: + DECLARE_STREAMABLE(); + + Shared username; + Shared receivers; + Shared permPolicy; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/InvocationMarshaller.cpp b/src/cpp/presents/data/InvocationMarshaller.cpp new file mode 100644 index 000000000..78a2342b6 --- /dev/null +++ b/src/cpp/presents/data/InvocationMarshaller.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "InvocationMarshaller.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.InvocationMarshaller", InvocationMarshaller); + +void InvocationMarshaller::readObject (ObjectInputStream& in) +{ + invOid = int32(in.readInt()); + invCode = int32(in.readInt()); +} + +void InvocationMarshaller::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(invOid); + out.writeInt(invCode); +} diff --git a/src/cpp/presents/data/InvocationMarshaller.h b/src/cpp/presents/data/InvocationMarshaller.h new file mode 100644 index 000000000..4c5c071f9 --- /dev/null +++ b/src/cpp/presents/data/InvocationMarshaller.h @@ -0,0 +1,27 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { + +class PresentsClient; + +namespace data { + +class InvocationMarshaller : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 invOid; + int32 invCode; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/PermissionPolicy.cpp b/src/cpp/presents/data/PermissionPolicy.cpp new file mode 100644 index 000000000..9583db76f --- /dev/null +++ b/src/cpp/presents/data/PermissionPolicy.cpp @@ -0,0 +1,14 @@ +#include "presents/stable.h" +#include "PermissionPolicy.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.PermissionPolicy", PermissionPolicy); + +void PermissionPolicy::readObject (ObjectInputStream& /*in*/) +{ +} + +void PermissionPolicy::writeObject (ObjectOutputStream& /*out*/) const +{ +} diff --git a/src/cpp/presents/data/PermissionPolicy.h b/src/cpp/presents/data/PermissionPolicy.h new file mode 100644 index 000000000..cf29ee0be --- /dev/null +++ b/src/cpp/presents/data/PermissionPolicy.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace data { + +class PermissionPolicy : public Streamable { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/data/RegistrationMarshaller.cpp b/src/cpp/presents/data/RegistrationMarshaller.cpp new file mode 100644 index 000000000..21262e3e7 --- /dev/null +++ b/src/cpp/presents/data/RegistrationMarshaller.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "RegistrationMarshaller.h" +#include "presents/PresentsClient.h" + +using namespace presents::data; + +DEFINE_STREAMABLE("com.threerings.presents.data.RegistrationMarshaller", RegistrationMarshaller); + +void RegistrationMarshaller::registerReceiver (Shared client, Shared arg1) +{ + typedef std::vector< Shared > StreamableList; + Shared args(new StreamableList); + args->push_back(arg1); + args->push_back(getSharedThis()); + client->sendRequest(invOid, invCode, 1, args); +} + +Shared RegistrationMarshaller::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/cpp/presents/data/RegistrationMarshaller.h b/src/cpp/presents/data/RegistrationMarshaller.h new file mode 100644 index 000000000..ae7cf856c --- /dev/null +++ b/src/cpp/presents/data/RegistrationMarshaller.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/streamers/StreamableStreamer.h" +#include +#include "presents/client/Registration.h" + +namespace presents { namespace data { + +class RegistrationMarshaller : public presents::data::InvocationMarshaller, public boost::enable_shared_from_this { +public: + DECLARE_STREAMABLE(); + + virtual ~RegistrationMarshaller () {} + + void registerReceiver (Shared client, Shared arg1); +protected: + Shared getSharedThis(); +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DEvent.cpp b/src/cpp/presents/dobj/DEvent.cpp new file mode 100644 index 000000000..7960e0f13 --- /dev/null +++ b/src/cpp/presents/dobj/DEvent.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DEvent", DEvent); + +void DEvent::readObject (ObjectInputStream& in) +{ + toid = int32(in.readInt()); +} + +void DEvent::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(toid); +} diff --git a/src/cpp/presents/dobj/DEvent.h b/src/cpp/presents/dobj/DEvent.h new file mode 100644 index 000000000..465fd6246 --- /dev/null +++ b/src/cpp/presents/dobj/DEvent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class DEvent : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 toid; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DObject.cpp b/src/cpp/presents/dobj/DObject.cpp new file mode 100644 index 000000000..3d77f7540 --- /dev/null +++ b/src/cpp/presents/dobj/DObject.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DObject.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DObject", DObject); + +void DObject::readObject (ObjectInputStream& in) +{ + oid = int32(in.readInt()); +} + +void DObject::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(oid); +} diff --git a/src/cpp/presents/dobj/DObject.h b/src/cpp/presents/dobj/DObject.h new file mode 100644 index 000000000..85b8500d9 --- /dev/null +++ b/src/cpp/presents/dobj/DObject.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class DObject : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 oid; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/DSet.cpp b/src/cpp/presents/dobj/DSet.cpp new file mode 100644 index 000000000..417bfc6e6 --- /dev/null +++ b/src/cpp/presents/dobj/DSet.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "DSet.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.DSet", DSet); + +void DSet::readObject (ObjectInputStream& in) +{ + size = in.readInt(); + entries = Shared > >(new std::vector< Shared >(size)); + for (int ii = 0; ii < size; ii++) { + (*entries)[ii] = boost::static_pointer_cast(in.readObject()); + } +} + +void DSet::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(size); + out.writeField(entries); +} diff --git a/src/cpp/presents/dobj/DSet.h b/src/cpp/presents/dobj/DSet.h new file mode 100644 index 000000000..7dd592d74 --- /dev/null +++ b/src/cpp/presents/dobj/DSet.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +namespace presents { namespace dobj { + +class DSet : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared< std::vector< Shared > > entries; + int32 size; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/EntryAddedEvent.cpp b/src/cpp/presents/dobj/EntryAddedEvent.cpp new file mode 100644 index 000000000..738e9e659 --- /dev/null +++ b/src/cpp/presents/dobj/EntryAddedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EntryAddedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.EntryAddedEvent", EntryAddedEvent); + +void EntryAddedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::NamedEvent::readObject(in); + entry = boost::static_pointer_cast(in.readObject()); +} + +void EntryAddedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::NamedEvent::writeObject(out); + out.writeObject(entry); +} diff --git a/src/cpp/presents/dobj/EntryAddedEvent.h b/src/cpp/presents/dobj/EntryAddedEvent.h new file mode 100644 index 000000000..1442887f0 --- /dev/null +++ b/src/cpp/presents/dobj/EntryAddedEvent.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/NamedEvent.h" +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class EntryAddedEvent : public presents::dobj::NamedEvent { +public: + DECLARE_STREAMABLE(); + + Shared entry; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/EntryRemovedEvent.cpp b/src/cpp/presents/dobj/EntryRemovedEvent.cpp new file mode 100644 index 000000000..f3f80527d --- /dev/null +++ b/src/cpp/presents/dobj/EntryRemovedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EntryRemovedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.EntryRemovedEvent", EntryRemovedEvent); + +void EntryRemovedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::NamedEvent::readObject(in); + key = boost::static_pointer_cast(in.readObject()); +} + +void EntryRemovedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::NamedEvent::writeObject(out); + out.writeObject(key); +} diff --git a/src/cpp/presents/dobj/EntryRemovedEvent.h b/src/cpp/presents/dobj/EntryRemovedEvent.h new file mode 100644 index 000000000..ec1795960 --- /dev/null +++ b/src/cpp/presents/dobj/EntryRemovedEvent.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/NamedEvent.h" +#include "presents/Streamable.h" + +namespace presents { namespace dobj { + +class EntryRemovedEvent : public presents::dobj::NamedEvent { +public: + DECLARE_STREAMABLE(); + + Shared key; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/InvocationNotificationEvent.cpp b/src/cpp/presents/dobj/InvocationNotificationEvent.cpp new file mode 100644 index 000000000..d50bd6c10 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationNotificationEvent.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "InvocationNotificationEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.InvocationNotificationEvent", InvocationNotificationEvent); + +void InvocationNotificationEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + receiverId = int16(in.readShort()); + methodId = int8(in.readByte()); + args = in.readField< std::vector< Shared > >(); +} + +void InvocationNotificationEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeShort(receiverId); + out.writeByte(methodId); + out.writeField(args); +} diff --git a/src/cpp/presents/dobj/InvocationNotificationEvent.h b/src/cpp/presents/dobj/InvocationNotificationEvent.h new file mode 100644 index 000000000..3018ba0e7 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationNotificationEvent.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class InvocationNotificationEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + int16 receiverId; + int8 methodId; + Shared< std::vector< Shared > > args; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/InvocationRequestEvent.cpp b/src/cpp/presents/dobj/InvocationRequestEvent.cpp new file mode 100644 index 000000000..e6fe2eaa5 --- /dev/null +++ b/src/cpp/presents/dobj/InvocationRequestEvent.cpp @@ -0,0 +1,22 @@ +#include "presents/stable.h" +#include "InvocationRequestEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.InvocationRequestEvent", InvocationRequestEvent); + +void InvocationRequestEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + invCode = int32(in.readInt()); + methodId = int8(in.readByte()); + args = in.readField< std::vector< Shared > >(); +} + +void InvocationRequestEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeInt(invCode); + out.writeByte(methodId); + out.writeField(args); +} diff --git a/src/cpp/presents/dobj/InvocationRequestEvent.h b/src/cpp/presents/dobj/InvocationRequestEvent.h new file mode 100644 index 000000000..4eee6c5ac --- /dev/null +++ b/src/cpp/presents/dobj/InvocationRequestEvent.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class InvocationRequestEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + int32 invCode; + int8 methodId; + Shared< std::vector< Shared > > args; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/dobj/NamedEvent.cpp b/src/cpp/presents/dobj/NamedEvent.cpp new file mode 100644 index 000000000..e2c0f2d7f --- /dev/null +++ b/src/cpp/presents/dobj/NamedEvent.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "NamedEvent.h" + +using namespace presents::dobj; + +DEFINE_STREAMABLE("com.threerings.presents.dobj.NamedEvent", NamedEvent); + +void NamedEvent::readObject (ObjectInputStream& in) +{ + presents::dobj::DEvent::readObject(in); + name = in.readField< utf8 >(); +} + +void NamedEvent::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DEvent::writeObject(out); + out.writeField(name); +} diff --git a/src/cpp/presents/dobj/NamedEvent.h b/src/cpp/presents/dobj/NamedEvent.h new file mode 100644 index 000000000..cb1018e65 --- /dev/null +++ b/src/cpp/presents/dobj/NamedEvent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DEvent.h" + +namespace presents { namespace dobj { + +class NamedEvent : public presents::dobj::DEvent { +public: + DECLARE_STREAMABLE(); + + Shared name; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/io/FramedInputStream.cpp b/src/cpp/presents/io/FramedInputStream.cpp new file mode 100644 index 000000000..ee3e36d85 --- /dev/null +++ b/src/cpp/presents/io/FramedInputStream.cpp @@ -0,0 +1,63 @@ +#include "FramedInputStream.h" + +#include "presents/stable.h" + +#include "presents/PresentsError.h" +#include "presents/Util.h" +#include "FramedInputStream.h" +#include "InputStream.h" + +using namespace presents; + +static const int READ_BUFFER_LENGTH = 4096; + +FramedInputStream::FramedInputStream (InputStream* base) +: _base(base) +, _length(-1) +, _fillPosition(0) +, _readPosition(0) +, _readBufferLength(READ_BUFFER_LENGTH) +, _readBuffer(new uint8[READ_BUFFER_LENGTH]) +{ +} + +// Returns bytes read +size_t FramedInputStream::read (void* pData, size_t bytesToRead) +{ + if (_fillPosition != _length) { + throw PresentsError("Attempted to read with no frame!"); + } + if ((int32) (_readPosition + bytesToRead) > _length) { + throw PresentsError("Attempted to read past frame!"); + } + memcpy(pData, &_readBuffer[_readPosition], bytesToRead); + _readPosition += bytesToRead; + return bytesToRead; +} + +bool FramedInputStream::readFrame () +{ + if (_length == _fillPosition) { + if (_fillPosition != _readPosition) { + throw PresentsError("Attempted to read new frame without consuming old one"); + } + _length = -1; + _fillPosition = _readPosition = 0; + } + if (_length == -1) { + _fillPosition += _base->read(&_readBuffer[_fillPosition], 4 - _fillPosition); + if (_fillPosition == 4) { + _length = ((_readBuffer[0] << 24) | (_readBuffer[1] << 16) | (_readBuffer[2] << 8) | _readBuffer[3]) - 4; + if (_length > _readBufferLength) { + _readBufferLength *= 2; + _readBuffer.reset(new uint8[_readBufferLength]); + PLOG("Thas a mighty big frame. Musta been feedin' him corn. Growing buffer to %d", _readBufferLength); + } + _fillPosition = 0; + } else { + return false; + } + } + _fillPosition += _base->read(&_readBuffer[_fillPosition], _length - _fillPosition); + return _fillPosition == _length; +} diff --git a/src/cpp/presents/io/FramedInputStream.h b/src/cpp/presents/io/FramedInputStream.h new file mode 100644 index 000000000..0a18c33fa --- /dev/null +++ b/src/cpp/presents/io/FramedInputStream.h @@ -0,0 +1,19 @@ +#pragma once + +#include "InputStream.h" + +namespace presents { + +class FramedInputStream : public InputStream +{ +public: + FramedInputStream (InputStream* base); + virtual size_t read(void* pData, size_t bytesToRead); + bool readFrame (); +protected: + int32 _length; + InputStream* _base; + boost::scoped_array _readBuffer; + int32 _fillPosition, _readPosition, _readBufferLength; +}; +} \ No newline at end of file diff --git a/src/cpp/presents/io/FramingOutputStream.cpp b/src/cpp/presents/io/FramingOutputStream.cpp new file mode 100644 index 000000000..540436c1a --- /dev/null +++ b/src/cpp/presents/io/FramingOutputStream.cpp @@ -0,0 +1,30 @@ +#include "presents/stable.h" +#include "FramingOutputStream.h" + +#include "presents/Util.h" + +using namespace presents; + +FramingOutputStream::FramingOutputStream (OutputStream* base) + : _base(base) +{} + +// Returns bytes written +size_t FramingOutputStream::write(const uint8* pData, size_t bytesToWrite) +{ + uint8* bytes = (uint8*)pData; + for (int ii = 0; ii < bytesToWrite; ii++) { + _frameBuffer.push_back(bytes[ii]); + } + return bytesToWrite; +} + + +void FramingOutputStream::writeFrame() +{ + int32 length = hostToPresents((int32)(_frameBuffer.size() + sizeof(int32))); + _base->write((uint8*)&length, sizeof(int32)); + _base->write(&_frameBuffer[0], _frameBuffer.size()); + + _frameBuffer.clear(); +} \ No newline at end of file diff --git a/src/cpp/presents/io/FramingOutputStream.h b/src/cpp/presents/io/FramingOutputStream.h new file mode 100644 index 000000000..23de81887 --- /dev/null +++ b/src/cpp/presents/io/FramingOutputStream.h @@ -0,0 +1,19 @@ +#pragma once + +#include "OutputStream.h" + +namespace presents { + +class FramingOutputStream : public OutputStream { +public: + FramingOutputStream (OutputStream* base); + + // Returns bytes written + virtual size_t write (const uint8* pData, size_t bytesToWrite); + void writeFrame (); + +protected: + OutputStream* _base; + std::vector _frameBuffer; +}; +} \ No newline at end of file diff --git a/src/cpp/presents/io/InputStream.h b/src/cpp/presents/io/InputStream.h new file mode 100644 index 000000000..304812b8d --- /dev/null +++ b/src/cpp/presents/io/InputStream.h @@ -0,0 +1,9 @@ +#pragma once + +namespace presents { +class InputStream { +public: + virtual ~InputStream() {}; + virtual size_t read(void* pData, size_t bytesToRead) = 0; +}; +} diff --git a/src/cpp/presents/io/OutputStream.h b/src/cpp/presents/io/OutputStream.h new file mode 100644 index 000000000..9b8610f64 --- /dev/null +++ b/src/cpp/presents/io/OutputStream.h @@ -0,0 +1,9 @@ +#pragma once + +namespace presents { +class OutputStream { +public: + virtual ~OutputStream() {}; + virtual size_t write(const uint8* pData, size_t bytesToWrite) = 0; +}; +} diff --git a/src/cpp/presents/net/AuthRequest.cpp b/src/cpp/presents/net/AuthRequest.cpp new file mode 100644 index 000000000..ca15a4d7f --- /dev/null +++ b/src/cpp/presents/net/AuthRequest.cpp @@ -0,0 +1,24 @@ +#include "presents/stable.h" +#include "AuthRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthRequest", AuthRequest); + +void AuthRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); + creds = boost::static_pointer_cast(in.readObject()); + version = in.readField< utf8 >(); + zone = in.readField< utf8 >(); + bootGroups = boost::static_pointer_cast< std::vector< Shared > >(in.readObject()); +} + +void AuthRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); + out.writeObject(creds); + out.writeField(version); + out.writeField(zone); + out.writeObject(bootGroups); +} diff --git a/src/cpp/presents/net/AuthRequest.h b/src/cpp/presents/net/AuthRequest.h new file mode 100644 index 000000000..9d736f5d3 --- /dev/null +++ b/src/cpp/presents/net/AuthRequest.h @@ -0,0 +1,26 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" +#include "presents/net/Credentials.h" + +namespace presents { namespace net { + +class AuthRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared creds; + Shared version; + Shared zone; + Shared< std::vector< Shared > > bootGroups; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/AuthResponse.cpp b/src/cpp/presents/net/AuthResponse.cpp new file mode 100644 index 000000000..c14d71e7a --- /dev/null +++ b/src/cpp/presents/net/AuthResponse.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "AuthResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthResponse", AuthResponse); + +void AuthResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + data = boost::static_pointer_cast(in.readObject()); +} + +void AuthResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(data); +} diff --git a/src/cpp/presents/net/AuthResponse.h b/src/cpp/presents/net/AuthResponse.h new file mode 100644 index 000000000..a9d2301fd --- /dev/null +++ b/src/cpp/presents/net/AuthResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/AuthResponseData.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class AuthResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared data; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/AuthResponseData.cpp b/src/cpp/presents/net/AuthResponseData.cpp new file mode 100644 index 000000000..156395f79 --- /dev/null +++ b/src/cpp/presents/net/AuthResponseData.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "AuthResponseData.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.AuthResponseData", AuthResponseData); + +void AuthResponseData::readObject (ObjectInputStream& in) +{ + presents::dobj::DObject::readObject(in); + code = in.readField< utf8 >(); +} + +void AuthResponseData::writeObject (ObjectOutputStream& out) const +{ + presents::dobj::DObject::writeObject(out); + out.writeField(code); +} diff --git a/src/cpp/presents/net/AuthResponseData.h b/src/cpp/presents/net/AuthResponseData.h new file mode 100644 index 000000000..fdadbb80a --- /dev/null +++ b/src/cpp/presents/net/AuthResponseData.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/dobj/DObject.h" + +namespace presents { namespace net { + +class AuthResponseData : public presents::dobj::DObject { +public: + DECLARE_STREAMABLE(); + + Shared code; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/BootstrapData.cpp b/src/cpp/presents/net/BootstrapData.cpp new file mode 100644 index 000000000..9b6d5e4ad --- /dev/null +++ b/src/cpp/presents/net/BootstrapData.cpp @@ -0,0 +1,21 @@ +#include "presents/stable.h" +#include "BootstrapData.h" +#include "presents/Streamer.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.BootstrapData", BootstrapData); + +void BootstrapData::readObject (ObjectInputStream& in) +{ + connectionId = int32(in.readInt()); + clientOid = int32(in.readInt()); + services = boost::static_pointer_cast< std::vector< Shared > >(in.readField(JAVA_LIST_NAME())); +} + +void BootstrapData::writeObject (ObjectOutputStream& out) const +{ + out.writeInt(connectionId); + out.writeInt(clientOid); + out.writeField(services, JAVA_LIST_NAME()); +} diff --git a/src/cpp/presents/net/BootstrapData.h b/src/cpp/presents/net/BootstrapData.h new file mode 100644 index 000000000..c98b2e6e0 --- /dev/null +++ b/src/cpp/presents/net/BootstrapData.h @@ -0,0 +1,25 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/data/InvocationMarshaller.h" +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class BootstrapData : public Streamable { +public: + DECLARE_STREAMABLE(); + + int32 connectionId; + int32 clientOid; + Shared< std::vector< Shared > > services; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/BootstrapNotification.cpp b/src/cpp/presents/net/BootstrapNotification.cpp new file mode 100644 index 000000000..3c7c09a3f --- /dev/null +++ b/src/cpp/presents/net/BootstrapNotification.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "BootstrapNotification.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.BootstrapNotification", BootstrapNotification); + +void BootstrapNotification::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + data = boost::static_pointer_cast(in.readObject()); +} + +void BootstrapNotification::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(data); +} diff --git a/src/cpp/presents/net/BootstrapNotification.h b/src/cpp/presents/net/BootstrapNotification.h new file mode 100644 index 000000000..55c58695a --- /dev/null +++ b/src/cpp/presents/net/BootstrapNotification.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/BootstrapData.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class BootstrapNotification : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared data; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/Credentials.cpp b/src/cpp/presents/net/Credentials.cpp new file mode 100644 index 000000000..7d02a2357 --- /dev/null +++ b/src/cpp/presents/net/Credentials.cpp @@ -0,0 +1,14 @@ +#include "presents/stable.h" +#include "Credentials.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.Credentials", Credentials); + +void Credentials::readObject (ObjectInputStream& /*in*/) +{ +} + +void Credentials::writeObject (ObjectOutputStream& /*out*/) const +{ +} diff --git a/src/cpp/presents/net/Credentials.h b/src/cpp/presents/net/Credentials.h new file mode 100644 index 000000000..73ce50e39 --- /dev/null +++ b/src/cpp/presents/net/Credentials.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class Credentials : public Streamable { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/DownstreamMessage.cpp b/src/cpp/presents/net/DownstreamMessage.cpp new file mode 100644 index 000000000..08782e9ff --- /dev/null +++ b/src/cpp/presents/net/DownstreamMessage.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "DownstreamMessage.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.DownstreamMessage", DownstreamMessage); + +void DownstreamMessage::readObject (ObjectInputStream& in) +{ + messageId = int16(in.readShort()); +} + +void DownstreamMessage::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(messageId); +} diff --git a/src/cpp/presents/net/DownstreamMessage.h b/src/cpp/presents/net/DownstreamMessage.h new file mode 100644 index 000000000..32cdea2fc --- /dev/null +++ b/src/cpp/presents/net/DownstreamMessage.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class DownstreamMessage : public Streamable { +public: + DECLARE_STREAMABLE(); + + int16 messageId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/EventNotification.cpp b/src/cpp/presents/net/EventNotification.cpp new file mode 100644 index 000000000..4458a0be1 --- /dev/null +++ b/src/cpp/presents/net/EventNotification.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "EventNotification.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.EventNotification", EventNotification); + +void EventNotification::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + event = boost::static_pointer_cast(in.readObject()); +} + +void EventNotification::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(event); +} diff --git a/src/cpp/presents/net/EventNotification.h b/src/cpp/presents/net/EventNotification.h new file mode 100644 index 000000000..faddeb045 --- /dev/null +++ b/src/cpp/presents/net/EventNotification.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/DownstreamMessage.h" +#include "presents/dobj/DEvent.h" + +namespace presents { namespace net { + +class EventNotification : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared event; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/ForwardEventRequest.cpp b/src/cpp/presents/net/ForwardEventRequest.cpp new file mode 100644 index 000000000..5c76acfd2 --- /dev/null +++ b/src/cpp/presents/net/ForwardEventRequest.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "ForwardEventRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.ForwardEventRequest", ForwardEventRequest); + +void ForwardEventRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); + event = boost::static_pointer_cast(in.readObject()); +} + +void ForwardEventRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); + out.writeObject(event); +} diff --git a/src/cpp/presents/net/ForwardEventRequest.h b/src/cpp/presents/net/ForwardEventRequest.h new file mode 100644 index 000000000..13d38b60d --- /dev/null +++ b/src/cpp/presents/net/ForwardEventRequest.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" +#include "presents/dobj/DEvent.h" + +namespace presents { namespace net { + +class ForwardEventRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared event; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/ObjectResponse.cpp b/src/cpp/presents/net/ObjectResponse.cpp new file mode 100644 index 000000000..387b10911 --- /dev/null +++ b/src/cpp/presents/net/ObjectResponse.cpp @@ -0,0 +1,18 @@ +#include "presents/stable.h" +#include "ObjectResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.ObjectResponse", ObjectResponse); + +void ObjectResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + dobj = boost::static_pointer_cast(in.readObject()); +} + +void ObjectResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeObject(dobj); +} diff --git a/src/cpp/presents/net/ObjectResponse.h b/src/cpp/presents/net/ObjectResponse.h new file mode 100644 index 000000000..f2d611bea --- /dev/null +++ b/src/cpp/presents/net/ObjectResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class ObjectResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + Shared dobj; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/PingRequest.cpp b/src/cpp/presents/net/PingRequest.cpp new file mode 100644 index 000000000..9a555b27c --- /dev/null +++ b/src/cpp/presents/net/PingRequest.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "PingRequest.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.PingRequest", PingRequest); + +void PingRequest::readObject (ObjectInputStream& in) +{ + presents::net::UpstreamMessage::readObject(in); +} + +void PingRequest::writeObject (ObjectOutputStream& out) const +{ + presents::net::UpstreamMessage::writeObject(out); +} diff --git a/src/cpp/presents/net/PingRequest.h b/src/cpp/presents/net/PingRequest.h new file mode 100644 index 000000000..89e4cc6eb --- /dev/null +++ b/src/cpp/presents/net/PingRequest.h @@ -0,0 +1,21 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/UpstreamMessage.h" + +namespace presents { namespace net { + +class PingRequest : public presents::net::UpstreamMessage { +public: + DECLARE_STREAMABLE(); + + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/PongResponse.cpp b/src/cpp/presents/net/PongResponse.cpp new file mode 100644 index 000000000..77b2da8d7 --- /dev/null +++ b/src/cpp/presents/net/PongResponse.cpp @@ -0,0 +1,20 @@ +#include "presents/stable.h" +#include "PongResponse.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.PongResponse", PongResponse); + +void PongResponse::readObject (ObjectInputStream& in) +{ + presents::net::DownstreamMessage::readObject(in); + packStamp = int64(in.readLong()); + processDelay = int32(in.readInt()); +} + +void PongResponse::writeObject (ObjectOutputStream& out) const +{ + presents::net::DownstreamMessage::writeObject(out); + out.writeLong(packStamp); + out.writeInt(processDelay); +} diff --git a/src/cpp/presents/net/PongResponse.h b/src/cpp/presents/net/PongResponse.h new file mode 100644 index 000000000..c06e7539e --- /dev/null +++ b/src/cpp/presents/net/PongResponse.h @@ -0,0 +1,23 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/net/DownstreamMessage.h" + +namespace presents { namespace net { + +class PongResponse : public presents::net::DownstreamMessage { +public: + DECLARE_STREAMABLE(); + + int64 packStamp; + int32 processDelay; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/UpstreamMessage.cpp b/src/cpp/presents/net/UpstreamMessage.cpp new file mode 100644 index 000000000..bb2d784ad --- /dev/null +++ b/src/cpp/presents/net/UpstreamMessage.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "UpstreamMessage.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.UpstreamMessage", UpstreamMessage); + +void UpstreamMessage::readObject (ObjectInputStream& in) +{ + messageId = int16(in.readShort()); +} + +void UpstreamMessage::writeObject (ObjectOutputStream& out) const +{ + out.writeShort(messageId); +} diff --git a/src/cpp/presents/net/UpstreamMessage.h b/src/cpp/presents/net/UpstreamMessage.h new file mode 100644 index 000000000..91eadf3b2 --- /dev/null +++ b/src/cpp/presents/net/UpstreamMessage.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace presents { namespace net { + +class UpstreamMessage : public Streamable { +public: + DECLARE_STREAMABLE(); + + int16 messageId; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/net/UsernamePasswordCreds.cpp b/src/cpp/presents/net/UsernamePasswordCreds.cpp new file mode 100644 index 000000000..8c31727b0 --- /dev/null +++ b/src/cpp/presents/net/UsernamePasswordCreds.cpp @@ -0,0 +1,20 @@ +#include "presents/stable.h" +#include "UsernamePasswordCreds.h" + +using namespace presents::net; + +DEFINE_STREAMABLE("com.threerings.presents.net.UsernamePasswordCreds", UsernamePasswordCreds); + +void UsernamePasswordCreds::readObject (ObjectInputStream& in) +{ + presents::net::Credentials::readObject(in); + username = boost::static_pointer_cast(in.readObject()); + password = in.readField< utf8 >(); +} + +void UsernamePasswordCreds::writeObject (ObjectOutputStream& out) const +{ + presents::net::Credentials::writeObject(out); + out.writeObject(username); + out.writeField(password); +} diff --git a/src/cpp/presents/net/UsernamePasswordCreds.h b/src/cpp/presents/net/UsernamePasswordCreds.h new file mode 100644 index 000000000..60898aa3a --- /dev/null +++ b/src/cpp/presents/net/UsernamePasswordCreds.h @@ -0,0 +1,24 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "util/Name.h" +#include "presents/net/Credentials.h" + +namespace presents { namespace net { + +class UsernamePasswordCreds : public presents::net::Credentials { +public: + DECLARE_STREAMABLE(); + + Shared username; + Shared password; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +}} \ No newline at end of file diff --git a/src/cpp/presents/platform/CFSocketStream.cpp b/src/cpp/presents/platform/CFSocketStream.cpp new file mode 100644 index 000000000..ddc501d80 --- /dev/null +++ b/src/cpp/presents/platform/CFSocketStream.cpp @@ -0,0 +1,187 @@ +// +// $Id$ + +#include "presents/stable.h" +#include "presents/SocketStream.h" +#include "presents/Util.h" +#include +#include + +namespace presents +{ + +class CFSocketStream : public SocketStream +{ +public: + CFSocketStream (const CFStringRef hostname, int port) + : _reader(NULL) + , _writer(NULL) + { + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, hostname, port, &_reader, &_writer); + CFReadStreamOpen(_reader); + CFWriteStreamOpen(_writer); + } + + virtual ~CFSocketStream () + { + CloseWithReason(CLIENT_CLOSED, "Destroyed!"); + } + + // Returns bytes read + virtual size_t read (void* pData, size_t bytesToRead) + { + if (_reader == NULL) { + PLOG("Attempted to read from closed stream!"); + return 0; + } + switch (CFReadStreamGetStatus(_reader)) { + case kCFStreamStatusClosed: + CloseWithReason(SERVER_DISCONNECT, "Server closed our read connection"); + return 0; + case kCFStreamStatusError: + HandleStreamError(CFReadStreamCopyError(_reader)); + return 0; + } + if (CFReadStreamHasBytesAvailable(_reader)) { + int read = CFReadStreamRead(_reader, (UInt8*)pData, bytesToRead); + if (read < 0) { + PLOG("Got error while reading!"); + HandleStreamError(CFReadStreamCopyError(_reader)); + return 0; + } else if (read == 0 && CFReadStreamGetStatus(_reader) == kCFStreamStatusAtEnd) { + // Only disconnect due to end-of-stream if the stream thinks there should be data + CloseWithReason(SERVER_DISCONNECT, "Hit read stream end"); + } + return read; + } + return 0; + } + + // Returns bytes written + virtual size_t write (const uint8* pData, size_t bytesToWrite) + { + if (_writer == NULL) { + PLOG("Attempted to write to closed stream!"); + return 0; + } + uint8* bytes = (uint8*)pData; + for (int ii = 0; ii < bytesToWrite; ii++) { + _writerBuffer.push_back(bytes[ii]); + } + return bytesToWrite; + } + + virtual void update (float dt) + { + if (_writer == NULL) { + PLOG("Attempted to write to closed stream!"); + return; + } + switch (CFWriteStreamGetStatus(_writer)) { + case kCFStreamStatusClosed: + CloseWithReason(SERVER_DISCONNECT, "Server closed our write connection"); + return; + case kCFStreamStatusAtEnd: + CloseWithReason(SERVER_DISCONNECT, "Hit write stream end"); + return; + case kCFStreamStatusError: + HandleStreamError(CFWriteStreamCopyError(_writer)); + return; + } + while (!_writerBuffer.empty() && CFWriteStreamCanAcceptBytes(_writer)) { + int written = CFWriteStreamWrite(_writer, (UInt8*)&_writerBuffer[0], _writerBuffer.size()); + if (written < 0) { + PLOG("Hit error in write!"); + HandleStreamError(CFWriteStreamCopyError(_writer)); + } else { + _writerBuffer.erase(_writerBuffer.begin(), _writerBuffer.begin() + written); + } + } + } + + virtual void close () + { + CloseWithReason(CLIENT_CLOSED, "Client requested close"); + } + + virtual connection_t onDisconnect (const DisconnectEvent::slot_type& slot) + { + return _onDisconnected.connect(slot); + } + +protected: + void CloseWithReason (presents::DisconnectReason why, const std::string& detail) + { + if (_reader == NULL) { + return;// Multiple closes don't hurt nobody + } + PLOG("Closing stream: %s", detail.c_str()); + CFReadStreamClose(_reader); + CFRelease(_reader); + _reader = NULL; + CFWriteStreamClose(_writer); + CFRelease(_writer); + _writer = NULL; + PLOG("Signaling disconnected"); + _onDisconnected(why); + } + + void HandleStreamError (CFErrorRef error) + { + if (CFErrorGetDomain(error) == kCFErrorDomainPOSIX) { + switch (CFErrorGetCode(error)) { + case ENETDOWN: + CloseWithReason(NETWORK_DISCONNECT, "Net down!"); + break; + case ENETUNREACH: + CloseWithReason(NETWORK_DISCONNECT, "Net unreachable"); + break; + case ENETRESET: + CloseWithReason(NETWORK_DISCONNECT, "Net reset"); + break; + case ECONNRESET: + CloseWithReason(NETWORK_DISCONNECT, "Net connection reset"); + break; + case EHOSTDOWN: + CloseWithReason(SERVER_UNREACHABLE, "host down!"); + break; + case EHOSTUNREACH: + CloseWithReason(SERVER_UNREACHABLE, "host unreachable!"); + break; + case ECONNREFUSED: + CloseWithReason(SERVER_UNREACHABLE, "connection refused"); + break; + case EPIPE: + CloseWithReason(SERVER_DISCONNECT, "broken pipe"); + break; + case ENOTCONN: + CloseWithReason(SERVER_DISCONNECT, "not connected"); + break; + default: + PLOG("Unknown posix net error: %d", CFErrorGetCode(error)); + CloseWithReason(NETWORK_DISCONNECT, "Unknown failure!"); + break; + } + } else { + PLOG("Unknown CFStreamError domain: domain: %s error: %d", CFErrorGetDomain(error), CFErrorGetCode(error)); + CloseWithReason(UNKNOWN, "CFStream error"); + } + CFRelease(error); + } + + CFWriteStreamRef _writer; + std::vector _writerBuffer; + CFReadStreamRef _reader; + DisconnectEvent _onDisconnected; +}; + +SocketStream* createSocketStream (const char* hostname, int port) +{ + CFStringRef cfHostname = CFStringCreateWithCString(kCFAllocatorDefault, hostname, + kCFStringEncodingMacRoman); + CFSocketStream* stream = new CFSocketStream(cfHostname, port); + CFRelease(cfHostname); + return stream; +} + +} diff --git a/src/cpp/presents/platform/NSLog.mm b/src/cpp/presents/platform/NSLog.mm new file mode 100644 index 000000000..33820c365 --- /dev/null +++ b/src/cpp/presents/platform/NSLog.mm @@ -0,0 +1,9 @@ +#include "presents/Util.h" +#undef interface + +#include + +void presents::log(const char* msg) +{ + NSLog(@"%s", msg); +} diff --git a/src/cpp/presents/stable.h b/src/cpp/presents/stable.h new file mode 100644 index 000000000..f09456c06 --- /dev/null +++ b/src/cpp/presents/stable.h @@ -0,0 +1,144 @@ +// +// C Library Headers +// +#include +#include +#include +#include + +// +// Standard C++ Headers +// +#include +#include +#include +#include + +// +// Boost Headers +// +#pragma warning(push, 3) + +#define BOOST_ALL_NO_LIB +#define BOOST_REGEX_NO_LIB +#define BOOST_DATE_TIME_NO_LIB + +#include +#include +#include + +// +// boost::signals +#pragma warning(push) +#pragma warning(disable: 4512) // assignment operator could not be generated +#include +#pragma warning(pop) + +// typedefs +typedef boost::signals::connection connection_t; +typedef boost::signals::scoped_connection scoped_connection_t; + +// GCC +#if defined(__GNUC__) +# define PRESENTS_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +// Visual Studio +#elif defined(_MSC_VER) +# define PRESENTS_COMPILER_MSVC _MSC_VER + +// Unknown Compiler +#else +# error The current compiler is not supported. +#endif + +#if defined(_WIN32) +# define PRESENTS_HOST_LITTLE_ENDIAN +typedef signed __int8 int8; +typedef unsigned __int8 uint8; +typedef signed __int16 int16; +typedef unsigned __int16 uint16; +typedef signed __int32 int32; +typedef unsigned __int32 uint32; +typedef signed __int64 int64; +typedef unsigned __int64 uint64; +typedef unsigned int uint; +typedef unsigned long ulong; + +#elif defined(__IPHONE__) +# define PRESENTS_IOS +#include +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#define _INT32 + +typedef unsigned int uint; +typedef unsigned long ulong; + +#elif defined(__APPLE__) +# define PRESENTS_MACOSX +#include + +#ifndef __i386__ +typedef SInt64 int64; +typedef UInt64 uint64; +typedef SInt32 int32; +typedef SInt16 int16; +typedef SInt8 int8; +typedef UInt32 uint32; +typedef UInt16 uint16; +typedef UInt8 uint8; +#else +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#define _INT32 +#endif + +typedef unsigned int uint; +typedef unsigned long ulong; + + +#elif defined(__linux__) +# define PRESENTS_HOST_LITTLE_ENDIAN +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef signed long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef unsigned int uint; +typedef unsigned long ulong; + +// Unknown Platform +#else +# error The current platform is not supported. +#endif + +#if defined(PRESENTS_IOS) || defined(PRESENTS_MACOSX) +// Apple defines __BIG_ENDIAN__ and __LITTLE_ENDIAN__ +# if defined(__BIG_ENDIAN__) +# define PRESENTS_HOST_BIG_ENDIAN +# else +# define PRESENTS_HOST_LITTLE_ENDIAN +# endif +#endif + +#define PLOG presents::internal::log + +#define Shared boost::shared_ptr + +#define utf8 std::string \ No newline at end of file diff --git a/src/cpp/presents/streamers/StreamableStreamer.h b/src/cpp/presents/streamers/StreamableStreamer.h new file mode 100644 index 000000000..ed2664e94 --- /dev/null +++ b/src/cpp/presents/streamers/StreamableStreamer.h @@ -0,0 +1,34 @@ +#pragma once + +#include "presents/Streamer.h" +#include "presents/Streamable.h" + +template +class StreamableStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in) + { + Shared streamable(new T()); + streamable->readObject(in); + return streamable; + } + + void writeObject (const Shared& object, ObjectOutputStream& out) + { + ((Shared&)object)->writeObject(out); + } +}; + +#define DECLARE_STREAMABLE() \ + static const utf8& javaName (); \ + static void registerWithPresents () { javaName(); } \ + virtual const utf8& getJavaClassName () const { return javaName(); } + +#define DEFINE_STREAMABLE(nameString, StreamableClass) \ + static const utf8& gStreamableClassRegistration = StreamableClass::javaName(); \ + const utf8& StreamableClass::javaName () {\ + static const utf8 JAVA_NAME = nameString; \ + static bool gRegistered = false; \ + if (!gRegistered) { registerStreamer(JAVA_NAME, new StreamableStreamer()); gRegistered = true; } \ + return JAVA_NAME; } diff --git a/src/cpp/presents/streamers/StringStreamer.cpp b/src/cpp/presents/streamers/StringStreamer.cpp new file mode 100644 index 000000000..15f08c247 --- /dev/null +++ b/src/cpp/presents/streamers/StringStreamer.cpp @@ -0,0 +1,17 @@ +#include "presents/stable.h" +#include "StringStreamer.h" + +#include + +#include "../ObjectInputStream.h" +#include "../ObjectOutputStream.h" + +Shared StringStreamer::createObject (ObjectInputStream& in) +{ + return Shared(new utf8(in.readUTF())); +} + +void StringStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + out.writeUTF(*(boost::static_pointer_cast(object))); +} diff --git a/src/cpp/presents/streamers/StringStreamer.h b/src/cpp/presents/streamers/StringStreamer.h new file mode 100644 index 000000000..abb07e429 --- /dev/null +++ b/src/cpp/presents/streamers/StringStreamer.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../Streamer.h" + +class StringStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in); + void writeObject (const Shared& object, ObjectOutputStream& out); +}; diff --git a/src/cpp/presents/streamers/VectorStreamer.h b/src/cpp/presents/streamers/VectorStreamer.h new file mode 100644 index 000000000..5b0720575 --- /dev/null +++ b/src/cpp/presents/streamers/VectorStreamer.h @@ -0,0 +1,223 @@ +#pragma once + +#include "../Streamer.h" +#include "presents/ArrayMask.h" + +template +class VectorStreamer : public Streamer +{ +public: + Shared createObject (ObjectInputStream& in); + void writeObject (const Shared& object, ObjectOutputStream& out); +}; + +template +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int length = in.readInt(); + Shared< std::vector< Shared > > v(new std::vector< Shared >(length)); + for (int ii = 0; ii < length; ++ii) { + Shared read = in.readObject(); + (*v)[ii] = boost::static_pointer_cast(read); + } + return v; +} + +template +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + Shared< std::vector< Shared > >& v = (Shared< std::vector< Shared > >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeObject((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readByte(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeByte((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readShort(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeShort((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readInt(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeInt((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readLong(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeLong((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readFloat(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeFloat((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readDouble(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeDouble((*v)[ii]); + } +} + +template <> +Shared VectorStreamer::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector > v(new std::vector(length)); + for (int ii = 0; ii < length; ++ii) { + (*v)[ii] = in.readBoolean(); + } + return v; +} + +template <> +void VectorStreamer::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector >& v = (Shared< std::vector >&)(object); + int length = v->size(); + out.writeInt(length); + for (int ii = 0; ii < length; ++ii) { + out.writeBoolean((*v)[ii]); + } +} + +template <> +Shared VectorStreamer< utf8 >::createObject (ObjectInputStream& in) +{ + int32 length = in.readInt(); + Shared< std::vector< Shared > > v(new std::vector< Shared >(length)); + ArrayMask mask(in); + for (int ii = 0; ii < length; ++ii) { + if (mask.isSet(ii)) { + (*v)[ii] = Shared(new utf8(in.readUTF())); + } else { + (*v)[ii] = Shared(); + } + } + return v; +} + +template <> +void VectorStreamer< utf8 >::writeObject (const Shared& object, ObjectOutputStream& out) +{ + const Shared< std::vector< Shared > >& v = (Shared< std::vector< Shared > >&)(object); + int length = v->size(); + out.writeInt(length); + ArrayMask mask(length); + for (int ii = 0; ii < length; ++ii) { + if ((*v)[ii] != NULL) { + mask.set(ii); + } + } + mask.writeTo(out); + for (int ii = 0; ii < length; ++ii) { + out.writeUTF(*((*v)[ii])); + } +} diff --git a/src/cpp/util/Name.cpp b/src/cpp/util/Name.cpp new file mode 100644 index 000000000..e19183ab8 --- /dev/null +++ b/src/cpp/util/Name.cpp @@ -0,0 +1,16 @@ +#include "presents/stable.h" +#include "Name.h" + +using namespace util; + +DEFINE_STREAMABLE("com.threerings.util.Name", Name); + +void Name::readObject (ObjectInputStream& in) +{ + name = in.readField< utf8 >(); +} + +void Name::writeObject (ObjectOutputStream& out) const +{ + out.writeField(name); +} diff --git a/src/cpp/util/Name.h b/src/cpp/util/Name.h new file mode 100644 index 000000000..0eb55c179 --- /dev/null +++ b/src/cpp/util/Name.h @@ -0,0 +1,22 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +#include "presents/Streamable.h" + +namespace util { + +class Name : public Streamable { +public: + DECLARE_STREAMABLE(); + + Shared name; + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +} \ No newline at end of file diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java b/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java new file mode 100644 index 000000000..62f2ebaf9 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPArgBuilder.java @@ -0,0 +1,71 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.util.List; + +import com.google.common.collect.Lists; + +import com.threerings.presents.tools.InvocationTask.ServiceMethod; + +public class CPPArgBuilder +{ + public CPPArgBuilder(boolean ignoreFirst) + { + _ignoreFirst = ignoreFirst; + } + + public String getArguments (ServiceMethod meth) + { + return getArguments(meth, ""); + } + + public String getArguments (ServiceMethod meth, String prefix) + { + StringBuilder buf = new StringBuilder(prefix); + Type[] ptypes = meth.method.getGenericParameterTypes(); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + if (buf.length() > 0) { + buf.append(", "); + } + buf.append(CPPUtil.getCPPType(ptypes[ii])).append(" arg").append(ii); + } + return buf.toString(); + } + + public List getArgumentNames (ServiceMethod meth) + { + Type[] ptypes = meth.method.getGenericParameterTypes(); + List args = Lists.newArrayListWithCapacity(ptypes.length - 1); + for (int ii = 1; ii < ptypes.length; ii++) { + args.add("arg" + ii); + } + return args; + } + + public String getArgumentsFromVector (ServiceMethod meth) + { + StringBuilder buf = new StringBuilder(); + Type[] ptypes = meth.method.getGenericParameterTypes(); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + if (buf.length() > 0) { + buf.append(", "); + } + CPPType type = new CPPType(ptypes[ii]); + buf.append(type.getCastFromStreamable("args[" + ii + "]")); + } + return buf.toString(); + } + + public List getServiceArguments (ServiceMethod meth) + { + Type[] ptypes = meth.method.getGenericParameterTypes(); + List args = Lists.newArrayListWithCapacity(ptypes.length); + for (int ii = _ignoreFirst ? 1 : 0; ii < ptypes.length; ii++) { + args.add(new CPPType(ptypes[ii]).getAsStreamable("arg" + ii)); + } + return args; + } + + protected final boolean _ignoreFirst; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPField.java b/src/main/java/com/threerings/presents/tools/cpp/CPPField.java new file mode 100644 index 000000000..8975f00b1 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPField.java @@ -0,0 +1,28 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Field; +import java.io.IOException; + +public class CPPField +{ + public final String name; + public final CPPType type; + public final String reader; + public final String writer; + + public CPPField (Field f) + throws IOException + { + type = new CPPType(f.getGenericType()); + name = f.getName().startsWith("_") ? f.getName().substring(1) : f.getName(); + if (type.fixed != null) { + reader = type.cast + "(in.read" + type.interpreter + "(" + type.fixed + "))"; + } else if (type.interpreter.equals("Field")) { + reader = "in.readField< " + type.getWithoutShared() + " >()"; + } else { + reader = type.cast + "(in.read" + type.interpreter + "())"; + } + writer = "out.write" + type.interpreter + "(" + name + + (type.fixed == null ? "" : ", " + type.fixed) + ")"; + } +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPType.java b/src/main/java/com/threerings/presents/tools/cpp/CPPType.java new file mode 100644 index 000000000..2196b1682 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPType.java @@ -0,0 +1,149 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.threerings.io.Streamable; +import com.threerings.presents.dobj.DSet; + +public class CPPType +{ + public static final String JAVA_LIST_FIXED = "JAVA_LIST_NAME()"; + + /** The full cpp type. */ + public final String type; + + /** The method on ObjectInputStream and ObjectOutputStream to interpret this type. */ + public final String interpreter; + + /** CPP code to cast from this object to its reader type. */ + public final String cast; + + /** If this is a fixed type and thereby doesn't need a type encoding on the wire. */ + public final String fixed; + + /** Another type embedded in this type, or null if it doesn't have an embedded type. */ + public final CPPType dependent; + + public final String representationImport; + + public final Class rawType; + + public final boolean primitive; + + public CPPType (Type javaType) + { + if (javaType instanceof ParameterizedType) { + rawType = (Class)((ParameterizedType)javaType).getRawType(); + } else if (javaType instanceof TypeVariable) { + rawType = (Class)((TypeVariable)javaType).getBounds()[0]; + } else { + rawType = (Class)javaType; + } + + if (rawType.equals(List.class)) { + fixed = JAVA_LIST_FIXED; + } else { + fixed = null; + } + + if (rawType.isArray()) { + Class componentType = rawType.getComponentType(); + dependent = new CPPType(componentType); + type = "Shared< std::vector< " + dependent.type + " > >"; + interpreter = componentType.equals(Object.class) || + componentType.isPrimitive() ? "Field" : "Object"; + representationImport = null; + + } else { + type = CPPUtil.getCPPType(javaType); + interpreter = getCPPInterpreter(rawType); + if (javaType instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType)javaType).getActualTypeArguments(); + if (rawType.equals(Comparable.class)) { + dependent = new CPPType(Streamable.class); + representationImport = "presents/Streamable.h"; + } else if (rawType == List.class) { + if (!Streamable.class.isAssignableFrom((Class)typeArguments[0])) { + throw new IllegalArgumentException( + "Lists may only contain Streamables in C++, not '" + typeArguments[0] + + "'"); + } + dependent = new CPPType(typeArguments[0]); + representationImport = null; + } else if (rawType.equals(DSet.class)) { + dependent = null; + representationImport = CPPUtil.makePath(rawType, ".h"); + } else { + throw new IllegalArgumentException("Don't know how to handle " + rawType); + } + } else if (javaType instanceof TypeVariable + || javaType.equals(com.threerings.presents.dobj.DSet.Entry.class)) { + dependent = new CPPType(Streamable.class); + representationImport = "presents/Streamable.h"; + } else { + if (!Streamable.class.equals(rawType) && Streamable.class.isAssignableFrom(rawType)) { + representationImport = CPPUtil.makePath(rawType, ".h"); + } else { + representationImport = null; + } + dependent = null; + } + } + + primitive = rawType.isPrimitive(); + Matcher m = TYPE_EXTRACT.matcher(type); + cast = m.matches() ? "boost::static_pointer_cast<" + m.group(1) + ">" : type; + } + + protected String getCPPInterpreter (Class ftype) + { + if (ftype.equals(String.class) || ftype.equals(List.class)) { + return "Field"; + } else if (ftype.equals(Boolean.TYPE)) { + return "Boolean"; + } else if (ftype.equals(Byte.TYPE)) { + return "Byte"; + } else if (ftype.equals(Short.TYPE)) { + return "Short"; + } else if (ftype.equals(Integer.TYPE)) { + return "Int"; + } else if (ftype.equals(Long.TYPE)) { + return "Long"; + } else if (ftype.equals(Float.TYPE)) { + return "Float"; + } else if (ftype.equals(Double.TYPE)) { + return "Double"; + } else { + return "Object"; + } + } + + public String getCastFromStreamable (String name) + { + if (primitive) { + return "boost::static_pointer_cast(" + name + ")->value"; + } + return cast + "(" + name + ")"; + } + + public String getAsStreamable (String name) + { + if (primitive) { + return "presents::box::Boxed" + interpreter + "::createShared(" + name + ")"; + } + return name; + } + + public String getWithoutShared () + { + Matcher m = TYPE_EXTRACT.matcher(type); + return m.matches() ? m.group(1) : type; + } + + protected static final Pattern TYPE_EXTRACT = Pattern.compile("Shared<(.*)>"); +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java b/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java new file mode 100644 index 000000000..83f8bbd06 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/CPPUtil.java @@ -0,0 +1,131 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import com.google.common.io.Resources; +import com.samskivert.mustache.Mustache; + +import com.threerings.presents.dobj.DSet; + +public class CPPUtil +{ + public static String getCPPType (Type ftype) + { + if (ftype.equals(com.threerings.presents.dobj.DSet.Entry.class)) { + return "Shared"; + } + if (ftype instanceof ParameterizedType) { + Type[] typeArguments = ((ParameterizedType)ftype).getActualTypeArguments(); + Type raw = ((ParameterizedType)ftype).getRawType(); + if (raw.equals(Comparable.class)) { + return "Shared"; + } else if (raw.equals(List.class)) { + return "Shared< std::vector< " + getCPPType(typeArguments[0]) + " > >"; + } else if (raw.equals(DSet.class)) { + ftype = raw; + } else { + throw new IllegalArgumentException("Don't know how to handle " + raw); + } + } + + if (ftype.equals(Boolean.class) || ftype.equals(Byte.class) || ftype.equals(Short.class) + || ftype.equals(Integer.class) || ftype.equals(Long.class) + || ftype.equals(Float.class) || ftype.equals(Double.class)) { + throw new IllegalArgumentException("Presents can't handle boxed types in C++"); + } + if (ftype.equals(String.class)) { + return "Shared"; + } else if (ftype.equals(Boolean.TYPE)) { + return "bool"; + } else if (ftype.equals(Byte.TYPE)) { + return "int8"; + } else if (ftype.equals(Short.TYPE)) { + return "int16"; + } else if (ftype.equals(Integer.TYPE)) { + return "int32"; + } else if (ftype.equals(Long.TYPE)) { + return "int64"; + } else if (ftype.equals(Float.TYPE)) { + return "float"; + } else if (ftype.equals(Double.TYPE)) { + return "double"; + } else if (ftype.equals(Object.class) || ftype instanceof TypeVariable) { + return "Shared"; + } else { + return "Shared<" + makeCPPName((Class)ftype) + ">"; + } + } + + public static void writeTemplate (String tmplPath, File root, String path, + Map ctx) + throws IOException + { + String tmpl = + Resources.toString(Resources.getResource(tmplPath), Charsets.UTF_8); + File file = new File(root, path); + file.getParentFile().mkdirs(); + BufferedWriter out = new BufferedWriter(new FileWriter(file)); + Mustache.compiler().escapeHTML(false).compile(tmpl).execute(ctx, out); + out.close(); + } + + public static String makeCPPName (Class sclass) + { + return makeCPPName(makeNamespaces(sclass), sclass.getSimpleName()); + } + + public static String makeCPPName (List namespaces, String className) + { + return Joiner.on("::").join(namespaces) + "::" + className; + } + + public static String makePath (Class klass, String ext) + { + return makePath(makeNamespaces(klass), klass.getSimpleName(), ext); + } + + public static String makePath (List namespaces, String className, String ext) + { + return Joiner.on(File.separator).join(namespaces) + File.separator + className+ ext; + } + + public static List makeNamespaces (String pack) + { + Iterable split = Splitter.on(".").split(pack); + List segs = Lists.newArrayList(split); + if (segs.size() > 1 && segs.get(0).equals("com") && segs.get(1).equals("threerings")) { + segs.remove(0); + segs.remove(0); + } + return segs; + + } + + public static List makeNamespaces (Class sclass) + { + if (sclass.getPackage() == null) { + return Collections.emptyList(); + } else { + return makeNamespaces(sclass.getPackage().getName()); + } + } + + public static String makeNamespace (Class sclass) + { + return Joiner.on("::").join(makeNamespaces(sclass)); + } +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java new file mode 100644 index 000000000..c2b3e06f6 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPReceiverTask.java @@ -0,0 +1,82 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java.io.File; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import com.threerings.presents.tools.GenReceiverTask; + +public class GenCPPReceiverTask extends GenReceiverTask +{ + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + protected void generateDecoder (Class receiver, File source, String rname, String rpackage, + List methods, Iterator imports, String rcode) + throws Exception + { + String dname = rname.replace("Receiver", "Decoder"); + + Map ctx = Maps.newHashMap(); + List namespaces = CPPUtil.makeNamespaces(receiver); + ctx.put("receiverName", rname); + ctx.put("decoderName", dname); + ctx.put("namespaces", namespaces); + ctx.put("namespace", CPPUtil.makeNamespace(receiver)); + ctx.put("package", rpackage); + ctx.put("methods", MethodDescriptor.from(methods)); + ctx.put("receiverCode", rcode); + ctx.put("argbuilder", new CPPArgBuilder(false)); + + CPPUtil.writeTemplate(DECODER_HEADER_TMPL, _cpproot, + CPPUtil.makePath(namespaces, dname, ".h"), ctx); + + Set receiverHeaderIncludes = Sets.newHashSet(); + Set decoderImplIncludes = Sets.newHashSet(); + for (ServiceMethod meth : methods) { + for (Type type : meth.method.getGenericParameterTypes()) { + CPPType cppType = new CPPType(type); + if (cppType.primitive) { + decoderImplIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h"); + } + while (cppType != null) { + if (cppType.representationImport != null) { + receiverHeaderIncludes.add(cppType.representationImport); + } + cppType = cppType.dependent; + } + } + } + + ctx.put("includes", decoderImplIncludes); + CPPUtil.writeTemplate(DECODER_CPP_TMPL, _cpproot, + CPPUtil.makePath(namespaces, dname, ".cpp"), ctx); + + + ctx.put("includes", receiverHeaderIncludes); + CPPUtil.writeTemplate(RECEIVER_HEADER_TMPL, _cpproot, + CPPUtil.makePath(namespaces, rname, ".h"), ctx); + + super.generateDecoder(receiver, source, rname, rpackage, methods, imports, rcode); + } + + protected File _cpproot; + + protected static final String RECEIVER_HEADER_TMPL = + "com/threerings/presents/tools/cpp/receiver_h.mustache"; + protected static final String DECODER_HEADER_TMPL = + "com/threerings/corpse/tools/decoder_h.mustache"; + protected static final String DECODER_CPP_TMPL = + "com/threerings/presents/tools/cpp/decoder_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java new file mode 100644 index 000000000..3f51701f0 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPServiceTask.java @@ -0,0 +1,73 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Type; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import com.threerings.presents.tools.GenServiceTask; + +import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces; +import static com.threerings.presents.tools.cpp.CPPUtil.makePath; +import static com.threerings.presents.tools.cpp.CPPUtil.writeTemplate; + +public class GenCPPServiceTask extends GenServiceTask +{ + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + protected void generateMarshaller (File source, ServiceDescription sdesc) + throws Exception + { + String name = sdesc.sname.replace("Service", "Marshaller"); + String mpackage = sdesc.spackage.replace(".client", ".data"); + + List namespaces = makeNamespaces(mpackage); + Map ctx = Maps.newHashMap(); + ctx.put("name", name); + ctx.put("javaName", mpackage + "." + name); + ctx.put("namespaces", namespaces); + ctx.put("namespace", Joiner.on("::").join(namespaces)); + ctx.put("methods", MethodDescriptor.from(sdesc.methods)); + ctx.put("listeners", sdesc.listeners); + ctx.put("argbuilder", new CPPArgBuilder(true)); + + Set includes = Sets.newHashSet(); + Set implIncludes = Sets.newHashSet(); + for (ServiceMethod meth : sdesc.methods) { + for (Type type : meth.method.getGenericParameterTypes()) { + CPPType cppType = new CPPType(type); + if (cppType.primitive) { + implIncludes.add("presents/box/Boxed" + cppType.interpreter + ".h"); + } + while (cppType != null) { + if (cppType.representationImport != null) { + includes.add(cppType.representationImport); + } + cppType = cppType.dependent; + } + } + } + ctx.put("includes", implIncludes); + writeTemplate(CPP_TMPL, _cpproot, makePath(namespaces, name, ".cpp"), ctx); + ctx.put("includes", includes); + writeTemplate(HEADER_TMPL, _cpproot, makePath(namespaces, name, ".h"), ctx); + super.generateMarshaller(source, sdesc); + } + + protected File _cpproot; + + protected static final String HEADER_TMPL = + "com/threerings/presents/tools/cpp/marshaller_h.mustache"; + protected static final String CPP_TMPL = + "com/threerings/presents/tools/cpp/marshaller_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java b/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java new file mode 100644 index 000000000..5d67e6696 --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/GenCPPStreamableTask.java @@ -0,0 +1,161 @@ +package com.threerings.presents.tools.cpp; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java.io.File; +import java.io.IOException; + +import org.apache.tools.ant.BuildException; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.threerings.io.SimpleStreamableObject; +import com.threerings.io.Streamable; + +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.DSet; +import com.threerings.presents.net.Message; +import com.threerings.presents.tools.GenTask; + +import static com.threerings.presents.tools.cpp.CPPUtil.makeCPPName; +import static com.threerings.presents.tools.cpp.CPPUtil.makeNamespaces; +import static com.threerings.presents.tools.cpp.CPPUtil.makePath; + +public class GenCPPStreamableTask extends GenTask +{ + public class Generate { + public void setClass (String name) + { + _toProcess.add(loadClass(name)); + } + } + + public Generate createGenerate () + { + return new Generate(); + } + + public void setCpproot (File asroot) + { + _cpproot = asroot; + } + + @Override + public void execute () + { + super.execute(); + for (Class klass : _toProcess) { + processClass(null, klass); + } + } + + @Override + protected void processClass (File fn, Class sclass) + { + try { + processClass(sclass); + } catch (IOException e) { + throw new BuildException(e); + } + } + + /** + * Processes a resolved Streamable class instance. + */ + protected void processClass (Class sclass) + throws IOException + { + if (!Streamable.class.isAssignableFrom(sclass) || + ((sclass.getModifiers() & Modifier.INTERFACE) != 0) || + DSet.class.equals(sclass) || + (InvocationMarshaller.class.isAssignableFrom(sclass) && !InvocationMarshaller.class.equals(sclass))) { +// System.err.println("Skipping " + sclass.getName() + "..."); + return; + } + + System.err.println("Generating " + sclass.getName()); + + // see if our parent also implements Streamable + boolean needSuper = Streamable.class.isAssignableFrom(sclass.getSuperclass()) && + !NONSUPER.contains(sclass.getSuperclass()); + + Map ctx = Maps.newHashMap(); + ctx.put("superclassStreamable", needSuper); + ctx.put("name", sclass.getSimpleName()); + ctx.put("namespaces", makeNamespaces(sclass)); + ctx.put("javaName", sclass.getName()); + ctx.put("namespace", CPPUtil.makeNamespace(sclass)); + + Set headerIncludes = Sets.newHashSet(); + Set implIncludes = Sets.newHashSet(); + if (needSuper) { + ctx.put("super", makeCPPName(sclass.getSuperclass())); + addInclude(sclass.getSuperclass(), headerIncludes); + } else { + ctx.put("super", "Streamable"); + headerIncludes.add("presents/Streamable.h"); + } + + List fields = Lists.newArrayList(); + ctx.put("fields", fields); + for (Field field : sclass.getDeclaredFields()) { + int mods = field.getModifiers(); + if (!Modifier.isStatic(mods) && !Modifier.isTransient(mods)) { + CPPField cppField = new CPPField(field); + fields.add(cppField); + CPPType type = cppField.type; + while (type != null) { + if (CPPType.JAVA_LIST_FIXED.equals(type.fixed)) { + implIncludes.add("presents/Streamer.h"); + } + if (type.representationImport != null) { + headerIncludes.add(type.representationImport); + } + type = type.dependent; + } + } + } + String inStream, outStream; + if (fields.isEmpty() && !needSuper) { + inStream = "/*in*/"; + outStream = "/*out*/"; + } else { + inStream = "in"; + outStream = "out"; + + } + ctx.put("inStreamArg", inStream); + ctx.put("outStreamArg", outStream); + + // now write all that out to the target source file + ctx.put("includes", headerIncludes); + CPPUtil.writeTemplate(HEADER_TMPL, _cpproot, makePath(sclass, ".h"), ctx); + + ctx.put("includes", implIncludes); + CPPUtil.writeTemplate(CPP_TMPL, _cpproot, makePath(sclass, ".cpp"), ctx); + } + + protected static void addInclude (Class ftype, Set includes) + { + includes.add(makePath(ftype, ".h")); + } + + protected File _cpproot; + + protected Set> _toProcess = Sets.newHashSet(); + + protected static final Set> NONSUPER = Sets.newHashSet(); + static { + NONSUPER.add(Message.class); + NONSUPER.add(SimpleStreamableObject.class); + } + protected static final String HEADER_TMPL = + "com/threerings/presents/tools/cpp/streamable_h.mustache"; + protected static final String CPP_TMPL = + "com/threerings/presents/tools/cpp/streamable_cpp.mustache"; +} diff --git a/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java b/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java new file mode 100644 index 000000000..be83ee6bc --- /dev/null +++ b/src/main/java/com/threerings/presents/tools/cpp/MethodDescriptor.java @@ -0,0 +1,32 @@ +package com.threerings.presents.tools.cpp; + +import java.util.List; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; + +import com.threerings.presents.tools.InvocationTask.ServiceMethod; + +public class MethodDescriptor +{ + public static List from(List methods) { + return Lists.transform(methods, new Function() { + @Override public MethodDescriptor apply (ServiceMethod from) { + return new MethodDescriptor(from); + }}); + } + + public final String methodName; + public final String vectorArguments; + public final String arguments; + public final String clientArguments; + public final List serviceArguments; + + public MethodDescriptor(ServiceMethod methodSource) { + methodName = methodSource.method.getName(); + vectorArguments = new CPPArgBuilder(false).getArgumentsFromVector(methodSource); + arguments = new CPPArgBuilder(false).getArguments(methodSource); + clientArguments = new CPPArgBuilder(true).getArguments(methodSource, "Shared client"); + serviceArguments = new CPPArgBuilder(true).getServiceArguments(methodSource); + } +} \ No newline at end of file diff --git a/src/main/resources/com/threerings/presents/tools.properties b/src/main/resources/com/threerings/presents/tools.properties index 6c63c9a68..acb1864ce 100644 --- a/src/main/resources/com/threerings/presents/tools.properties +++ b/src/main/resources/com/threerings/presents/tools.properties @@ -3,3 +3,6 @@ genservice=com.threerings.presents.tools.GenServiceTask genreceiver=com.threerings.presents.tools.GenReceiverTask instream=com.threerings.presents.tools.InstrumentStreamableTask genascript=com.threerings.presents.tools.GenActionScriptTask +gencppservice=com.threerings.presents.tools.cpp.GenCPPServiceTask +gencppstreamable=com.threerings.presents.tools.cpp.GenCPPStreamableTask +gencppreceiver=com.threerings.presents.tools.cpp.GenCPPReceiverTask diff --git a/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache new file mode 100644 index 000000000..534bdd7b1 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/decoder_cpp.mustache @@ -0,0 +1,28 @@ +#include "presents/stable.h" +#include "{{decoderName}}.h" +#include "{{receiverName}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +{{decoderName}}::{{decoderName}} (Shared<{{receiverName}}> receiver) : + InvocationDecoder("{{receiverCode}}"), + _receiver(receiver) +{ +} + +void {{decoderName}}::dispatchNotification(int8 methodId, const std::vector< Shared >& args) +{ + switch(methodId) { +{{#methods}} + case {{-index}}: + _receiver->{{methodName}} ( {{vectorArguments}} ); + return; +{{/methods}} + default: + LOG_INFO("{{decoderName}} got unknown method: %d", methodId); + return; + } +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache new file mode 100644 index 000000000..b11b07f9b --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/decoder_h.mustache @@ -0,0 +1,20 @@ +#pragma once + +#include "presents/InvocationDecoder.h" + +{{#namespaces}} +namespace {{this}} { {{/namespaces}} + +class {{receiverName}}; + +class {{decoderName}} : public presents::InvocationDecoder { +public: + {{decoderName}}(Shared<{{receiverName}}> receiver); + void dispatchNotification(int8 methodId, const std::vector< Shared >& args); + +protected: + Shared<{{receiverName}}> _receiver; +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache new file mode 100644 index 000000000..3cf2ef08e --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_cpp.mustache @@ -0,0 +1,28 @@ +#include "presents/stable.h" +#include "{{name}}.h" +#include "presents/PresentsClient.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +DEFINE_STREAMABLE("{{javaName}}", {{name}}); + +{{#methods}} +void {{name}}::{{methodName}} ({{clientArguments}}) +{ + typedef std::vector< Shared > StreamableList; + Shared args(new StreamableList); +{{#serviceArguments}} + args->push_back({{this}}); +{{/serviceArguments}} + args->push_back(getSharedThis()); + client->sendRequest(invOid, invCode, {{-index}}, args); +} + +{{/methods}} +Shared<{{name}}> {{name}}::getSharedThis() +{ + return shared_from_this(); +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache new file mode 100644 index 000000000..453452661 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/marshaller_h.mustache @@ -0,0 +1,30 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/data/InvocationMarshaller.h" +#include "presents/streamers/StreamableStreamer.h" +#include +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}}namespace {{this}} { {{/namespaces}} + + +class {{name}} : public presents::data::InvocationMarshaller, public boost::enable_shared_from_this<{{name}}> { +public: + DECLARE_STREAMABLE(); + + virtual ~{{name}} () {} + +{{#methods}} + void {{methodName}} ({{clientArguments}}); +{{/methods}} +protected: + Shared<{{name}}> getSharedThis(); +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache new file mode 100644 index 000000000..4f5d3f4e0 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/receiver_h.mustache @@ -0,0 +1,21 @@ +#pragma once + +#include "{{decoderName}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}}namespace {{this}} { {{/namespaces}} + + +class {{receiverName}} { +public: + typedef {{decoderName}} Decoder; + +{{#methods}} + virtual void {{methodName}} ({{arguments}}) = 0; +{{/methods}} +}; + +{{#namespaces}} +}{{/namespaces}} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache b/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache new file mode 100644 index 000000000..fed50e4ae --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/streamable_cpp.mustache @@ -0,0 +1,29 @@ +#include "presents/stable.h" +#include "{{name}}.h" +{{#includes}} +#include "{{this}}" +{{/includes}} + +using namespace {{namespace}}; + +DEFINE_STREAMABLE("{{javaName}}", {{name}}); + +void {{name}}::readObject (ObjectInputStream& {{inStreamArg}}) +{ +{{#superclassStreamable}} + {{super}}::readObject(in); +{{/superclassStreamable}} +{{#fields}} + {{name}} = {{reader}}; +{{/fields}} +} + +void {{name}}::writeObject (ObjectOutputStream& {{outStreamArg}}) const +{ +{{#superclassStreamable}} + {{super}}::writeObject(out); +{{/superclassStreamable}} +{{#fields}} + {{writer}}; +{{/fields}} +} diff --git a/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache b/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache new file mode 100644 index 000000000..3ddac4ad5 --- /dev/null +++ b/src/main/resources/com/threerings/presents/tools/cpp/streamable_h.mustache @@ -0,0 +1,29 @@ +#pragma once + +#include "presents/Streamable.h" +#include "presents/ObjectInputStream.h" +#include "presents/ObjectOutputStream.h" +#include "presents/streamers/StreamableStreamer.h" + +{{#includes}} +#include "{{this}}" +{{/includes}} + +{{#namespaces}} +namespace {{this}} { {{/namespaces}} + + +class {{name}} : public {{super}} { +public: + DECLARE_STREAMABLE(); + +{{#fields}} + {{type.type}} {{name}}; +{{/fields}} + + virtual void readObject(ObjectInputStream& in); + virtual void writeObject(ObjectOutputStream& out) const; +}; + +{{#namespaces}} +}{{/namespaces}}