Optimization generally means punching through layers and putting knowledge in places where it doesn't normally belong.
You have to make some trade-offs. Is it worth it saving a few bytes, and having to put knowledge of the protocol into the network class? If not, then you should length-prefix each packet, and you'll be fine. Have the sender put in length, type, payload, and you're good. The network class can then de-frame packets to the type,payload level, and pass that on to the registered handler for each type.
class PacketHandler {
public:
virtual void HandlePacket(size_t size, void const *data) = 0;
};
class Network {
public:
void AddPacketHandler(int type, PacketHandler *handler);
private:
size_t CheckBuffer(size_t size, void const *data);
};
size_t Network::CheckBuffer(size_t size, void const *data) {
size_t packetsize = 0;
size_t offset = 0;
if (!TryDecodeSize(size, data, &offset, &packetsize)) {
return 0;
}
if (size - offset < packetsize) {
return 0;
}
int type = 0;
size_t typesize;
if (!TryDecodeType(size-offset, (char *)data + offset, &typesize, &type)) {
return packetsize + offset; // bad packet, skip past it
}
PacketHandler *handler = LookupTypeHandler(type);
if (!handler) {
return packetsize + offset; // unknown packet, skip it
}
handler->HandlePacket(packetsize - typesize, (char *)data + offset + typesize);
return packetsize + offset;
}
If saving that length byte (or two?) matters, then you have to do something like registering type-specific protocol decoders with the networking subsystem. You'd then find the decoder class for the given type code, after you've decoded the type code, and ask that class whether there's enough data to actually dispatch the packet. This means the decoder code may run several times for a given packet before you have enough data to actually dispatch the packet.
class PacketDecoder {
public:
virtual bool CanDecodePacket(size_t available, void const *start, size_t *o_size) = 0;
};
class PacketHandler {
public:
virtual void HandlePacket(size_t size, void const *data) = 0;
};
class Network {
public:
void AddPacketDecoder(int type, PacketDecoder *decoder);
void AddPacketHandler(int type, PacketHandler *handler);
private:
bool CheckBuffer(size_t size, void const *data) = 0;
};
size_t Network::CheckBuffer(size_t size, void const *data) {
int type = 0;
size_t typesize;
if (!TryDecodeType(size, (char *)data, &typesize, &type)) {
return 0; // don't have type yet
}
PacketDecoder *decoder = LookupTypeDecoder(type);
if (!decoder) {
exit(1); // the connection is in a bad state and there is no way to recover
}
size_t packetsize = 0;
if (!decoder->CanDecodePacket(size - typesize, (char *)data + typesize), &packetsize) {
return 0; // can't decode packet yet
}
PacketHandler *handler = LookupTypeHandler(type);
if (!handler) {
exit(1); // the network system is mis-configured and can't proceed
}
handler->HandlePacket(packetsize, (char *)data + typesize);
return packetsize + typesize; // packet consumed
}