ostinato/common/abstractprotocol.cpp
Srivats P. 1357f495ac Major rewrite of the protocol framework - changes not yet complete
Common
------
- Change in OstProto
	- Individual protocols are now extensions of (new) message 'Protocol' instead of 'Stream'
	- Stream now contains a repeated Protocol which also determines the ordered set of currently selected protocols; StreamCore.frame_proto which was doing this earlier has been removed

- Change in AbstractProtocol Interface
	- Parent changed to StreamBase
	- Corresponding change in constructor and factory func - createInstance()
	- new method protocolNumber() - returns unique id for each protocol
	- protoDataCopyInto/From() now copies into OstProto::Protocol instead of OstProto::Stream
- Change in all subclasses of AbstractProtocol to match new interface
- For all protocols, configFrom is no longer static, but each protocol has its own configForm
	- configForm creation is lazy
	- configForm is still a child of the protocol i.e. it will be destroyed alongwith the protocol
	- TODO: convert configWidget() to a pure factory function i.e. the protocol does not own the configForm - this requires us to pass the widget into load/storeConfigWidget() methods

- ProtocolCollection class removed alongwith its .h and .cpp
- ProtocolList class redefined to serve the purpose of ProtocolCollection
- New class ProtocolListIterator defined to iterate ProtocolList
- AbstractProtocol methods now use the ProtocolListIterator
- Factory function createProtocol() added to ProtocolManager
- OstProto::StreamCore accessor functions moved from Stream to StreamBase

Server
------
- MyService uses the newly moved accessors to StreamBase for OstProto::StreamCore members
- StreamInfo now uses the protocols to create the packet

Client
------
- StreamConfigDialog now uses ProtocolListIterator
- So does PacketModel
2009-08-02 14:52:34 +00:00

476 lines
11 KiB
C++

#include <qendian.h>
#include "abstractprotocol.h"
#include "streambase.h"
#include "protocollistiterator.h"
/*!
\class AbstractProtocol
// FIXME - update this text
Bare Minimum set of methods that a subclass needs to reimplement
- protoDataCopyInto() [pure virtual]
- protoDataCopyFrom() [pure virtual]
- fieldCount()
Any useful protocol should also provide implementations for
- name()
- shortName()
- fieldName()
Protocols with meta fields should additionally implement
- metaFieldCount()
- isMetaField()
*/
AbstractProtocol::AbstractProtocol(StreamBase *stream)
{
mpStream = stream;
metaCount = -1;
protoSize = -1;
}
AbstractProtocol::~AbstractProtocol()
{
}
AbstractProtocol* AbstractProtocol::createInstance(StreamBase *stream)
{
return NULL;
}
quint32 AbstractProtocol::protocolNumber() const
{
qDebug("Something wrong!!!");
return 0xFFFFFFFF;
}
/*!
\fn virtual void protoDataCopyInto(OstProto::OstProto::StreamCore &stream) = 0;
Copy the protocol's protobuf into the passed in stream \n
In the base class this is a pure virtual function. Subclasses should
implement this function by using - \n
stream.AddExtension(<ExtId>)->CopyFrom(<protobuf_data>) */
/*
\fn virtual void protoDataCopyFrom(const OstProto::OstProto::StreamCore &stream) = 0;
FIXME */
/*! Returns the full name of the protocol \n
The default implementation returns a null string */
QString AbstractProtocol::name() const
{
return QString();
}
/*! Returns the short name or abbreviation of the protocol \n
The default implementation forms and returns a abbreviation composed
of all the upper case chars in name() \n
The default implementation caches the abbreviation on its first invocation
and subsequently returns the cached abbreviation */
QString AbstractProtocol::shortName() const
{
if (protoAbbr.isNull())
{
QString abbr;
for (int i = 0; i < name().size(); i++)
if (name().at(i).isUpper()) abbr.append(name().at(i));
if (abbr.size())
protoAbbr = abbr;
else
protoAbbr = QString("");
}
return protoAbbr;
}
/*! Returns the number of fields (both Frame and Meta fields) \n
The default implementation returns zero */
int AbstractProtocol::fieldCount() const
{
return 0;
}
/*! Returns the number of meta fields \n
The default implementation counts and returns the number of fields for which
the FieldIsMeta flag is set\n
The default implementation caches the count on its first invocation
and subsequently returns the cached count */
int AbstractProtocol::metaFieldCount() const
{
if (metaCount < 0)
{
int c = 0;
for (int i = 0; i < fieldCount() ; i++)
if (fieldFlags(i).testFlag(FieldIsMeta))
c++;
metaCount = c;
}
return metaCount;
}
/*! Returns the number of frame fields \n
Convenience method - same as fieldCount() minus metaFieldCount() */
int AbstractProtocol::frameFieldCount() const
{
//qDebug("%s:%d, %d", __FUNCTION__, fieldCount(), metaFieldCount());
return (fieldCount() - metaFieldCount());
}
AbstractProtocol::FieldFlags AbstractProtocol::fieldFlags(int index) const
{
return FieldIsNormal;
}
/*! Returns the requested field attribute data \n
Protocols which have meta fields that vary a frame field across
streams may use the streamIndex to return the appropriate field value \n
Some field attriubutes e.g. FieldName may be invariant across streams\n
The default implementation returns the fieldValue() converted to string
The FieldTextValue attribute may include additional information about
the field's value e.g. a checksum field may include "(correct)" or
"(incorrect)" alongwith the actual checksum value. \n
The default implementation returns FIXME
\note If a subclass uses any of the below functions to derive
FieldFrameValue, the subclass should handle and return a value for
FieldBitSize to prevent endless recrusion -
- protocolFrameCksum()
*/
QVariant AbstractProtocol::fieldData(int index, FieldAttrib attrib,
int streamIndex) const
{
switch (attrib)
{
case FieldName:
return QString();
case FieldBitSize:
Q_ASSERT_X(!fieldFlags(index).testFlag(FieldIsCksum),
"AbstractProtocol::fieldData()",
"FieldBitSize for checksum fields need to be handled by the subclass");
return fieldData(index, FieldFrameValue, streamIndex).
toByteArray().size() * 8;
case FieldValue:
return 0;
case FieldFrameValue:
return QByteArray();
case FieldTextValue:
return QString();
default:
qFatal("%s:%d: unhandled case %d\n", __FUNCTION__, __LINE__,
attrib);
}
return QVariant();
}
/*! Sets the value of a field corresponding to index \n
Returns true if field is successfully set, false otherwise \n
The default implementation always returns false */
bool AbstractProtocol::setFieldData(int index, const QVariant &value,
FieldAttrib attrib)
{
return false;
}
quint32 AbstractProtocol::protocolId(ProtocolIdType type) const
{
return 0;
}
quint32 AbstractProtocol::payloadProtocolId(ProtocolIdType type) const
{
quint32 id = 0xFFFFFFFF;
ProtocolListIterator *iter = mpStream->createProtocolListIterator();
if (iter->findNext(this))
{
if (iter->hasNext())
id = iter->next()->protocolId(type);
}
delete iter;
qDebug("%s: payloadProtocolId = %u", __FUNCTION__, id);
return id;
}
int AbstractProtocol::protocolFrameSize() const
{
if (protoSize < 0)
{
int bitsize = 0;
for (int i = 0; i < fieldCount(); i++)
{
if (!fieldFlags(i).testFlag(FieldIsMeta))
bitsize += fieldData(i, FieldBitSize).toUInt();
}
protoSize = (bitsize+7)/8;
}
qDebug("%s: protoSize = %d", __FUNCTION__, protoSize);
return protoSize;
}
int AbstractProtocol::protocolFrameOffset() const
{
int size = 0;
ProtocolListIterator *iter = mpStream->createProtocolListIterator();
if (iter->findNext(this))
{
iter->previous();
while (iter->hasPrevious())
size += iter->previous()->protocolFrameSize();
}
else
return -1;
delete iter;
qDebug("%s: ofs = %d", __FUNCTION__, size);
return size;
}
int AbstractProtocol::protocolFramePayloadSize() const
{
int size = 0;
ProtocolListIterator *iter = mpStream->createProtocolListIterator();
if (iter->findNext(this))
{
while (iter->hasNext())
size += iter->next()->protocolFrameSize();
}
else
return -1;
delete iter;
qDebug("%s: payloadSize = %d", __FUNCTION__, size);
return size;
}
/*! Returns a byte array encoding the protocol (and its fields) which can be
inserted into the stream's frame
The default implementation forms and returns an ordered concatenation of
the FrameValue of all the 'frame' fields of the protocol taking care of fields
which are not an integral number of bytes\n */
QByteArray AbstractProtocol::protocolFrameValue(int streamIndex, bool forCksum) const
{
QByteArray proto, field;
uint bits, lastbitpos = 0;
FieldFlags flags;
for (int i=0; i < fieldCount() ; i++)
{
flags = fieldFlags(i);
if (!flags.testFlag(FieldIsMeta))
{
bits = fieldData(i, FieldBitSize, streamIndex).toUInt();
Q_ASSERT(bits > 0);
if (forCksum && flags.testFlag(FieldIsCksum))
{
field.resize((bits+7)/8);
field.fill('\0');
}
else
field = fieldData(i, FieldFrameValue, streamIndex).toByteArray();
qDebug("<<< %d, %d/%d >>>>", proto.size(), bits, field.size());
if (bits == (uint) field.size() * 8)
{
if (lastbitpos == 0)
proto.append(field);
else
{
Q_ASSERT(field.size() > 0);
char c = proto[proto.size() - 1];
proto[proto.size() - 1] = c | (field.at(0) >> lastbitpos);
for (int j = 0; j < field.size() - 1; j++)
proto.append(field.at(j) << lastbitpos |
field.at(j+1) >> lastbitpos);
}
}
else if (bits < (uint) field.size() * 8)
{
uchar c;
uint v;
v = (field.size()*8) - bits;
Q_ASSERT(v < 8);
if (lastbitpos == 0)
{
for (int j = 0; j < field.size(); j++)
{
c = field.at(j) << v;
if ((j+1) < field.size())
c |= ((uchar)field.at(j+1) >> (8-v));
proto.append(c);
}
lastbitpos = (lastbitpos + bits) % 8;
}
else
{
Q_ASSERT(proto.size() > 0);
for (int j = 0; j < field.size(); j++)
{
uchar d;
c = field.at(j) << v;
if ((j+1) < field.size())
c |= ((uchar) field.at(j+1) >> (8-v));
d = proto[proto.size() - 1];
proto[proto.size() - 1] = d | ((uchar) c >> lastbitpos);
if (bits > (8*j + (8 - v)))
proto.append(c << (8-lastbitpos));
}
lastbitpos = (lastbitpos + bits) % 8;
}
}
else // if (bits > field.size() * 8)
{
qFatal("bitsize more than FrameValue size. skipping...");
continue;
}
}
}
return proto;
}
/*!
\note If a subclass uses protocolFrameCksum() from within fieldData() to
derive a cksum field, it MUST handle and return the 'FieldBitSize'
attribute also for that particular field instead of using the default
AbstractProtocol implementation for 'FieldBitSize' - this is required
to prevent infinite recursion
*/
quint32 AbstractProtocol::protocolFrameCksum(int streamIndex,
CksumType cksumType) const
{
static int recursionCount = 0;
quint32 cksum = 0;
recursionCount++;
Q_ASSERT_X(recursionCount < 10, "protocolFrameCksum", "potential infinite recursion - does a protocol checksum field not implement FieldBitSize?");
switch(cksumType)
{
case CksumIp:
{
QByteArray fv;
quint16 *ip;
quint32 len, sum = 0;
fv = protocolFrameValue(streamIndex, true);
ip = (quint16*) fv.constData();
len = fv.size();
while(len > 1)
{
sum += *ip;
if(sum & 0x80000000)
sum = (sum & 0xFFFF) + (sum >> 16);
ip++;
len -= 2;
}
if (len)
sum += (unsigned short) *(unsigned char *)ip;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
cksum = qFromBigEndian((quint16) ~sum);
break;
}
case CksumTcpUdp:
{
quint16 cks;
quint32 sum = 0;
cks = protocolFrameCksum(streamIndex, CksumIp);
sum += (quint16) ~cks;
cks = protocolFramePayloadCksum(streamIndex, CksumIp);
sum += (quint16) ~cks;
cks = protocolFrameHeaderCksum(streamIndex, CksumIpPseudo);
sum += (quint16) ~cks;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
cksum = (~sum) & 0xFFFF;
break;
}
default:
break;
}
recursionCount--;
return cksum;
}
quint32 AbstractProtocol::protocolFrameHeaderCksum(int streamIndex,
CksumType cksumType) const
{
quint32 sum = 0xFFFF;
ProtocolListIterator *iter = mpStream->createProtocolListIterator();
Q_ASSERT(cksumType == CksumIpPseudo);
if (iter->findNext(this))
{
iter->previous();
if (iter->hasPrevious())
sum = iter->previous()->protocolFrameCksum(streamIndex,
CksumIpPseudo);
}
delete iter;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return (quint16) ~sum;
}
quint32 AbstractProtocol::protocolFramePayloadCksum(int streamIndex,
CksumType cksumType) const
{
quint32 sum = 0;
quint16 cksum;
ProtocolListIterator *iter = mpStream->createProtocolListIterator();
Q_ASSERT(cksumType == CksumIp);
if (iter->findNext(this))
{
while (iter->hasNext())
{
cksum = iter->next()->protocolFrameCksum(streamIndex, CksumIp);
sum += (quint16) ~cksum;
}
}
else
return 0;
delete iter;
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return (quint16) ~sum;
}