Cast object from long in safe manner?

J

Jamie Burns

Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was casted into
an object, and this object was accessed, the application would crash hard or
potentially exhibit strangeness from memory corruption.

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.

Quick example of what I mean:

------------------

class A {
// ...
virtual foo() {}
}

A* a = new A();

long objectID = (long) a;

A* a2 = dynamic_cast<A*>((void *) objectID);
 
P

Patrik Stellmann

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.
One idea would be not to use "long integer" but a pointer to a base
class of all your objects. than a dynamic_cast would work pretty well
(although it's quite slow).
 
H

Heinz Ozwirk

im Newsbeitrag : Hello,
:
: I am writing a client / server application. There is 1 server, and many
: clients. The server processes requests from each client, and typically
: creates and manipulates C++ objects on their behalf.
:
: Now, when a client requests for an object to be created, I pass back a
: pointer to the object (from server memory address scope) as a "long
: integer". To the client, this is just an ID for the object they wish to
: access. From then on, when clients wish an operation to be performed on an
: object, they pass back the appropriate "long integer" and the manipulation
: they wish to perform. The server then casts the "long integer" into the
: object it expects, and performs the manipulation.
:
: This is working quite well, only it is obviously quite dangerous. If a
: client was to pass back a different "long integer", and this was casted into
: an object, and this object was accessed, the application would crash hard or
: potentially exhibit strangeness from memory corruption.
:
: How can I get my "long integer" back into an object in a safe manner? I
: tried dynamic_cast but it wouldn't work.

It's never really safe to cast a pointer to long and back to pointer. A
pointer may be too large to fit into a long or someone might have
modified the long value as you suspect.

I would prefer to have a server app that keeps its objects together with
some id in some collection (probably a map) and only passes the id to the
client. When it recieves an id from the client, the server can easyly
search the map and find the corresponding object (if the id is valid).

Regards
Heinz
 
R

Rolf Magnus

Jamie said:
Hello,

I am writing a client / server application. There is 1 server, and
many clients. The server processes requests from each client, and
typically creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish
to access. From then on, when clients wish an operation to be
performed on an object, they pass back the appropriate "long integer"
and the manipulation they wish to perform. The server then casts the
"long integer" into the object it expects, and performs the
manipulation.

This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was
casted into an object, and this object was accessed, the application
would crash hard or potentially exhibit strangeness from memory
corruption.

How can I get my "long integer" back into an object in a safe manner?
I tried dynamic_cast but it wouldn't work.

dynamic_cast only works for casting within a class hierarchy of a
polymorphic class. It needs some information about the type that is
stored in the object. But there is no void object, so you can't
dynamic_cast from a void*.
I would step away from using the pointer itself as ID, and rather
maintain a list (maybe std::vector) of pointers to your objects, and
then provide an index into that as ID for the objects. Then your check
reduces to a simple bounds check.
 
M

Maciej Sobczak

Hi,

Jamie said:
I am writing a client / server application. [...]

How can I get my "long integer" back into an object in a safe manner?

I would certainly recommend that you do not return to the clients a raw
pointer (and this is what you do, even if the clients think it is ID).

It would be better if the server, after creating new object, assigned a
true ID to it and create some mapping from ID to pointer (class std::map
springs to mind). Then, the clients gets the ID, not the pointer.

Of course, the mapping is cleared when the object is destroyed, so that
at any time the map contains only IDs (and pointers) to the living objects.

When the client requests some operation on the object, it gives you the
ID. Instead of just using the pointer (which is unsafe), the server
first has to find the "true" pointer that belongs to the given ID and
then uses the pointer. In this additional step, you may find that there
is no pointer in the map for requested ID, which can mean that the
object was already destroyed or that the client just gives you rubbish.
This is exactly the place where you get the safety, because if there is
no mapping, you can reply to the client that there is "no such object",
instead of naively trying to use it and crashing.

If you think that the mapping would add additional overhead to the
server, it will not. The communication part (networking) is already
heavy enough so that the additional step on the server side is negligible.


You may find the following project interesting:

http://www.msobczak.com/prog/yami/

This is a messaging infrastructure that can be very useful in writing
client/server systems.
 
G

Gary

Jamie Burns said:
Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

Set up a mapping in the server of an arbitrary long integer to each object
and pass this integer to the client. When the client calls using the long
integer, the server looks up which object it refers to. You have re-invented
handles. It has some advantage, since if the object is copied or moved in
the course of an operation the handle stays the same for the client. Use a
map to associate the long integer to the object. Forget casting.
 
P

Peter Koch Larsen

Jamie Burns said:
Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was casted into
an object, and this object was accessed, the application would crash hard or
potentially exhibit strangeness from memory corruption.

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.

You can't. Instead, you could have e.g. a map of < int, class * > and do a
look-up of the pointer. One more example of "an extra indirection solves
everything".

/Peter

[example snipped]
 
B

Ben Hutchings

Jamie said:
Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

Casting pointers to integers and back is non-portable, though it
generally works with sufficiently large integers. It will probably
be easier to secure the server if you assign IDs that have nothing
to do with object addresses.
This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was casted into
an object, and this object was accessed, the application would crash hard or
potentially exhibit strangeness from memory corruption.

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.
<snip>

Maintain a std::set of valid object addresses. Or, if you assign
IDs separately from addresses, use a std::map or std::vector to map
IDs to addresses.

You will probably want to prevent clients from interfering with
each other, in which case you will need to maintain one map per
connection.
 
D

Dhruv

Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was casted into
an object, and this object was accessed, the application would crash hard or
potentially exhibit strangeness from memory corruption.

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.

Quick example of what I mean:

------------------

class A {
// ...
virtual foo() {}
}

A* a = new A();

long objectID = (long) a;

A* a2 = dynamic_cast<A*>((void *) objectID);

I think that dynamic_cast is only for up casting, and that too ONLY in
cases where virtual bases are involved (I might be wrong on that count,
because I haven't really used it ever!).

However, for your purposes, a simple, crude and potentially unsafe
reinterpret_cast would do fine. Just add this statament just before making
the cast:

STATIC_ASSERT (sizeof(long)) == sizeof(A*));

Where STATIC_ASSERT is some static assertion template. You can get it
from boost, or create it yourself like this:

template <bool B>
struct SAssert { };

template <>
struct SAssert<false>;

#define STATIC_ASSERT(EXPR) { SAssert<EXPR> SA_Obj; }



Anyways, why do you want to do such things? Why not just pass the pointer
as a pointer to A?


Regards,
-Dhruv.
 
J

Jamie Burns

Yes, this is a good idea, but I am concerned about performance. The idea in
sending the pointer was to reduce overhead when a client makes a call on an
object. How fast is performing a lookup using a map in comparison to casting
a pointer? Each process can have a lot of server objects (thus the map would
have to be efficient when has a lot of elements in it).

I have implemented a piece of code now that bascially means I send the
client a pointer to a struct which looks like:

struct ObjectHolder {
unsigned long signature;
MyObject* object;
};

And when they make a call I do this (basically):

ObjectHolder objectHolder = (ObjectHolder*) message.objectId;
if (objectHolder && objectHolder->signature == this->clientSignature) {

MySuperObject* superObject =
dynamic_cast<MySuperObject*>(objectHolder->object);
if (superObject) superObject->doAction(message.data);

}

The struct has an unsigned long in it which is basically a magic string, and
if this checks out it tells me the client owns the object holder, and that
it is indeed an object holder. From there I cast the MyObject into
MySuperObject (which is now allowed whereas I never actually passed this
pointer back and forth).

Of course, there is the very slim chance that if a client passes in a rouge
ObjectHolder pointer (objectId), there could conincedentally be a 4 byte
value matching the "signature" in question at the address of the rogue
pointer, but this has to be millions to one.

Can you see any real problems with this? If a rogue ObjectHolder pointer was
passed, surely the signature should show it to be rogue?

I just think that on a server app which will be processing bucket loads of
these messages, looking up in a map for every message will be a massive
performance drain.

Jamie.
 
M

Martijn Lievaart

Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was casted into
an object, and this object was accessed, the application would crash hard or
potentially exhibit strangeness from memory corruption.

How can I get my "long integer" back into an object in a safe manner? I
tried dynamic_cast but it wouldn't work.

Yes, this is indeed very dangerous. How to solve it depends a bit on your
needs.

There is no good way to solve this and maintain speed, but you can get
close. I assume you have way of identifying connections, say by a pointer
to a connection object.

When creating the object you want to pass a handle to the client, don't
pass the pointer itself. Instead, create an entry in some array where you
store the connection and the pointer to the object. Return to the client
the index into this array.

When the client wants to refer to an object, it passes back the index. Now
you can easily get back the pointer to the object, but also can you easily
check if the object was indeed one for this connection.

An alternative would be to keep an array of objects passed out for any
connection in the connection object itself. Give the client back the index
into this array. The big advantage of this aproach is that it can also
ease memory management. If the connection dies, you know which related
objects to delete.

HTH,
M4
 
K

Kevin Cline

Jamie Burns said:
Hello,

I am writing a client / server application. There is 1 server, and many
clients. The server processes requests from each client, and typically
creates and manipulates C++ objects on their behalf.

Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish to
access. From then on, when clients wish an operation to be performed on an
object, they pass back the appropriate "long integer" and the manipulation
they wish to perform. The server then casts the "long integer" into the
object it expects, and performs the manipulation.

This is working quite well, only it is obviously quite dangerous... >
class A {
// ...
virtual foo() {}
}
static long next_a_id = 0;
static std::map said:
A* a = new A();
// > long objectID = (long) a;
a_map[++next_a_id] = a;
return next_a_id;
 
K

kanze

I am writing a client / server application. There is 1 server, and
many clients. The server processes requests from each client, and
typically creates and manipulates C++ objects on their behalf.
Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer". To the client, this is just an ID for the object they wish
to access. From then on, when clients wish an operation to be
performed on an object, they pass back the appropriate "long integer"
and the manipulation they wish to perform. The server then casts the
"long integer" into the object it expects, and performs the
manipulation.

What happens if the server goes down, then comes back up in the
mean-time (reloading the objects it was managing from persistency)? If
your server doesn't support this now, don't worry. It will have to some
time in the future.

An address is a fugitive thing. Never, never let one escape from a
process.
This is working quite well, only it is obviously quite dangerous. If a
client was to pass back a different "long integer", and this was
casted into an object, and this object was accessed, the application
would crash hard or potentially exhibit strangeness from memory
corruption.
How can I get my "long integer" back into an object in a safe manner?

You can't.

The only real solution here is to generate arbitrary identifiers, and
use a map.
 
T

Thomas Mang

Dhruv said:
I think that dynamic_cast is only for up casting, and that too ONLY in
cases where virtual bases are involved (I might be wrong on that count,
because I haven't really used it ever!).

You are wrong.

First, up-casting is implicitly done. What you probably mean is downcasting.

Second, virtual bases have nothing to do with it. What is required is available
RTTI - so at least one virtual function in the class which acts as source for the
cast. That's why a void* can't be the source.

And third, with dynamic_cast you can also cross-cast. For example, try out the
following snippet:


#include <iostream>
#include <ostream>

struct base_a
{
virtual ~base_a(){}
};

struct base_b
{
virtual ~base_b(){}
};

struct derived : public base_a, public base_b
{};

int main(int argc, char* argv[])
{
base_a * b = new derived;

if (base_b * b2 =dynamic_cast<base_b*>(b))
std::cout << "cast successful";

return 0;
}


You will find out that the cast succeeds.


regards,

Thomas
 
P

Peter Koch Larsen

Jamie Burns said:
Yes, this is a good idea, but I am concerned about performance. The idea in
sending the pointer was to reduce overhead when a client makes a call on an
object. How fast is performing a lookup using a map in comparison to casting
a pointer? Each process can have a lot of server objects (thus the map would
have to be efficient when has a lot of elements in it).

The map is O(log n) in nature, which is not that bad.
I have implemented a piece of code now that bascially means I send the
client a pointer to a struct which looks like:

struct ObjectHolder {
unsigned long signature;
MyObject* object;
};

And when they make a call I do this (basically):

ObjectHolder objectHolder = (ObjectHolder*) message.objectId;
if (objectHolder && objectHolder->signature == this->clientSignature) {

This will not be good if the client sends a wrong idea. The referenced
object will point at a random location and anything might happen.
MySuperObject* superObject =
dynamic_cast<MySuperObject*>(objectHolder->object);
if (superObject) superObject->doAction(message.data);

}

The struct has an unsigned long in it which is basically a magic string, and
if this checks out it tells me the client owns the object holder, and that
it is indeed an object holder. From there I cast the MyObject into
MySuperObject (which is now allowed whereas I never actually passed this
pointer back and forth).

Of course, there is the very slim chance that if a client passes in a rouge
ObjectHolder pointer (objectId), there could conincedentally be a 4 byte
value matching the "signature" in question at the address of the rogue
pointer, but this has to be millions to one.

But the client could easily send a pointer to some invalid
memory-location or (worse!) to a valid memorylocation allocated by
another client.
Can you see any real problems with this? If a rogue ObjectHolder pointer was
passed, surely the signature should show it to be rogue?

I just think that on a server app which will be processing bucket loads of
these messages, looking up in a map for every message will be a massive
performance drain.
Test it first! Programmers somehow always believe the bottlenecks to
be in the wrong places.

As an alternative, i propose two alternative solutions:

a) use a hash_map. Not standard yet, but it very soon will be. Lookup
is O(1) amortized.
b) use a vector of class* and return the index of that vector. Look-up
will again be O(1). In this case, i would like to verify that the
client is allowed to access that index. This could be done by having
two integers to identify the class... the first being the index, the
second being some magical number which you could produce yourself in
any reasonable way.
 
S

Stefan Rupp

Good evening,

not a C++ related answer, but for the questioner a hint nevertheless!

Jamie said:
I am writing a client / server application. [...]
Now, when a client requests for an object to be created, I pass back a
pointer to the object (from server memory address scope) as a "long
integer".

Assuming your clients communicates with your server via network:

Aside from the other good advice you got, please keep in mind that the
byteorder of any multibyte entity like a 32bit integer value you send
across the net might be different on the client and server side. For
example an Intel i386 based system has a different byteorder for 32bit
integers as e.g. a MC68000 based system.

However, there is the defined "network byte order", into and out of
which values can be translated for purposes of sending data across
networks in heterogenous environments. The keywords you need to search
for are: htonl and ntohl!

Regards,
Stefan
 
R

Rolf Magnus

Please don't top-post. Put your answers below the text you're answering
to, and quote only relevant parts.

[mod note: top posting may be frowned upon, but is not a moderation
consideration.]

Jamie said:
Yes, this is a good idea, but I am concerned about performance.

Your network may be slower than the lookup in a map.
The idea in sending the pointer was to reduce overhead when a client
makes a call on an object. How fast is performing a lookup using a map
in comparison to casting a pointer?

It certainly is slower, but you might want to benchmark it to see if it
has a relevant impact on performance.
Each process can have a lot of
server objects (thus the map would have to be efficient when has a lot
of elements in it).

Should a client only access its own objects or all? If the former, I'd
do one map per client, which would reduce the number of entries in each
of those maps.
I have implemented a piece of code now that bascially means I send the
client a pointer to a struct

Didn't you get enough answers that told you to _never_ send a pointer?
Instead of your MyObject pointers you now transfer ObjectHolder
pointers, which doesn't really improve the situation.
which looks like:

struct ObjectHolder {
unsigned long signature;
MyObject* object;
};

And when they make a call I do this (basically):

ObjectHolder objectHolder = (ObjectHolder*) message.objectId;
if (objectHolder && objectHolder->signature ==
this->clientSignature) {

MySuperObject* superObject =
dynamic_cast<MySuperObject*>(objectHolder->object);
if (superObject) superObject->doAction(message.data);

}

The above code suffers from the same problem as the orignal code. If the
client sends garbage, your server will crash, because the pointer
points "into outer space". Note that this is not the same as a null
pointer, so your check doesn't grab that situation. Further,
transfering your pointer as a long isn't portable. On many 64 bit
platforms, a long is 32 bit, but a pointer is 64 bit.
The struct has an unsigned long in it which is basically a magic
string, and if this checks out it tells me the client owns the object
holder, and that it is indeed an object holder. From there I cast the
MyObject into MySuperObject (which is now allowed whereas I never
actually passed this pointer back and forth).

Of course, there is the very slim chance that if a client passes in a
rouge ObjectHolder pointer (objectId), there could conincedentally be
a 4 byte value matching the "signature" in question at the address of
the rogue pointer, but this has to be millions to one.

There is a much bigger chance that your client passes an invalid pointer
value that makes your program try to access memory that doesn't belong
to it. And on most modern OSs, this results in the kernel terminating
your program immediately.
Can you see any real problems with this? If a rogue ObjectHolder
pointer was passed, surely the signature should show it to be rogue?

I just think that on a server app which will be processing bucket
loads of these messages, looking up in a map for every message will be
a massive performance drain.

As I have pointed out, you could use a vector instead, which means using
the id as index. This should not be noticably slower than directly
using the pointer. Removing objects could be a bit tricky, though, but
there is a variety of different possible solutions to that.
 
R

Rolf Magnus

Stefan said:
Good evening,

not a C++ related answer, but for the questioner a hint nevertheless!

Jamie said:
I am writing a client / server application. [...]
Now, when a client requests for an object to be created, I pass
back a pointer to the object (from server memory address scope) as
a "long integer".

Assuming your clients communicates with your server via network:

Aside from the other good advice you got, please keep in mind that the
byteorder of any multibyte entity like a 32bit integer value you send
across the net might be different on the client and server side. For
example an Intel i386 based system has a different byteorder for 32bit
integers as e.g. a MC68000 based system.

However, there is the defined "network byte order", into and out of
which values can be translated for purposes of sending data across
networks in heterogenous environments. The keywords you need to search
for are: htonl and ntohl!

Though what you say is correct, it doesn't seem very relevant for the
question, since the client is not doing anything with the received
value other than sending it back to the server it came from.
 
S

Stefan Rupp

Good afternoon,

Rolf said:
Though what you say is correct, it doesn't seem very relevant for the
question, since the client is not doing anything with the received
value other than sending it back to the server it came from.

If the pointer is the only data sent between Server and Client, yes.
But I assume there is other data to be sent across the network where the
pointer serves only as the ID (where a logical ID would certainly server
better as all posters agree). For the other data the byte order may
play an important role, except if the data is sent as a byte stream like
strings. Any kind of numerical value with more than 8 bits needs to be
converted into and out of network byte order before sending through the
network (or after receiving it, respectilvely).

Regards,
Stefan
 
J

James Kanze

|> Yes, this is a good idea, but I am concerned about performance. The
|> idea in sending the pointer was to reduce overhead when a client
|> makes a call on an object. How fast is performing a lookup using a
|> map in comparison to casting a pointer? Each process can have a lot
|> of server objects (thus the map would have to be efficient when has
|> a lot of elements in it).

|> I have implemented a piece of code now that bascially means I send
|> the client a pointer to a struct which looks like:

|> struct ObjectHolder {
|> unsigned long signature;
|> MyObject* object;
|> };

|> And when they make a call I do this (basically):

|> ObjectHolder objectHolder = (ObjectHolder*) message.objectId;
|> if (objectHolder && objectHolder->signature == this->clientSignature) {

|> MySuperObject* superObject =
|> dynamic_cast<MySuperObject*>(objectHolder->object);
|> if (superObject) superObject->doAction(message.data);
|>
|> }

|> The struct has an unsigned long in it which is basically a magic
|> string, and if this checks out it tells me the client owns the
|> object holder, and that it is indeed an object holder. From there I
|> cast the MyObject into MySuperObject (which is now allowed whereas I
|> never actually passed this pointer back and forth).

Who cares? If the client sends an invalid pointer, you might very well
get a core dump.

Letting pointers escape from a process, and hoping to use them later,
simply doesn't work. Period. It's a bad idea, and you don't want to do
it.

|> Of course, there is the very slim chance that if a client passes in
|> a rouge ObjectHolder pointer (objectId), there could conincedentally
|> be a 4 byte value matching the "signature" in question at the
|> address of the rogue pointer, but this has to be millions to one.

And there is an almost certainty that the client will, at one point or
another, return something which doesn't point to valid memory, and
causes your server to crash. Immediately invalidating all of the
pointers in all of the clients, so that further client requests will
cause the restarted server to crash, ad infinitum.

|> Can you see any real problems with this? If a rogue ObjectHolder
|> pointer was passed, surely the signature should show it to be rogue?

Just that it doesn't work.

|> I just think that on a server app which will be processing bucket
|> loads of these messages, looking up in a map for every message will
|> be a massive performance drain.

And marshalling and demarshalling won't. And the network itself won't.

Who are you kidding?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top