NORM-mirror/common/normMessage.h

1621 lines
56 KiB
C++

#ifndef _NORM_MESSAGE
#define _NORM_MESSAGE
// PROTOLIB includes
#include "protokit.h"
// standard includes
#include <string.h> // for memcpy(), etc
#include <math.h>
#include <stdlib.h> // for rand(), etc
#ifdef _WIN32_WCE
#include <stdio.h>
#else
#include <sys/types.h> // for off_t
#endif // if/else _WIN32_WCE
#ifdef SIMULATE
#define SIM_PAYLOAD_MAX (36+8) // MGEN message size + StreamPayloadHeaderLen()
#endif // SIMULATE
const UINT8 NORM_PROTOCOL_VERSION = 1;
const int NORM_ROBUST_FACTOR = 20; // default robust factor
// This value is used in a couple places in the code as
// a safety check where some critical timeouts may be
// less than expected operating system clock resolution
const double NORM_TICK_MIN = 0.100; // in seconds
// Pick a random number from 0..max
inline double UniformRand(double max)
{
return (max * ((double)rand() / (double)RAND_MAX));
}
// Pick a random number from 0..max
// (truncated exponential dist. lambda = log(groupSize) + 1)
inline double ExponentialRand(double max, double groupSize)
{
double lambda = log(groupSize) + 1;
double x = UniformRand(lambda/max)+lambda/(max*(exp(lambda)-1));
return ((max/lambda)*log(x*(exp(lambda)-1)*(max/lambda)));
}
// These are the GRTT estimation bounds set for the current
// NORM protocol. (Note that our Grtt quantization routines
// are good for the range of 1.0e-06 <= 1000.0)
const double NORM_GRTT_MIN = 0.001; // 1 msec
const double NORM_GRTT_MAX = 15.0; // 15 sec
const double NORM_RTT_MIN = 1.0e-06;
const double NORM_RTT_MAX = 1000.0;
inline double NormUnquantizeRtt(unsigned char qrtt)
{
return ((qrtt < 31) ?
(((double)(qrtt+1))*(double)NORM_RTT_MIN) :
(NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
}
unsigned char NormQuantizeRtt(double rtt);
inline double NormUnquantizeGroupSize(unsigned char gsize)
{
double exponent = (double)((gsize & 0x07) + 1);
double mantissa = (0 != (gsize & 0x08)) ? 5.0 : 1.0;
return (mantissa * pow(10.0, exponent));
}
inline unsigned char NormQuantizeGroupSize(double gsize)
{
unsigned char ebits = (unsigned char)log10(gsize);
int mantissa = (int)((gsize/pow(10.0, (double)ebits)) + 0.5);
// round up quantized group size
unsigned char mbit = (mantissa > 1) ? ((mantissa > 5) ? 0x00 : 0x08) : 0x00;
ebits = (mantissa > 5) ? ebits : ebits+1;
mbit = (ebits > 0x07) ? 0x08 : mbit;
ebits = (ebits > 0x07) ? 0x07 : ebits;
return (mbit | ebits);
}
inline unsigned short NormQuantizeLoss(double lossFraction)
{
lossFraction = MAX(lossFraction, 0.0);
lossFraction = lossFraction*65535.0 + 0.5;
lossFraction = MIN(lossFraction, 65535.0);
return (unsigned short)lossFraction;
} // end NormQuantizeLossFraction()
inline double NormUnquantizeLoss(unsigned short lossQuantized)
{
return (((double)lossQuantized) / 65535.0);
} // end NormUnquantizeLossFraction()
inline unsigned short NormQuantizeRate(double rate)
{
unsigned char exponent = (unsigned char)log10(rate);
unsigned short mantissa = (unsigned short)((256.0/10.0) * rate / pow(10.0, (double)exponent));
return ((mantissa << 8) | exponent);
}
inline double NormUnquantizeRate(unsigned short rate)
{
double mantissa = ((double)(rate >> 8)) * (10.0/256.0);
double exponent = (double)(rate & 0xff);
return mantissa * pow(10.0, exponent);
}
class NormObjectSize
{
public:
#ifdef WIN32
typedef __int64 Offset;
#else
typedef off_t Offset;
#endif
NormObjectSize() : size(0) {}
NormObjectSize(Offset theSize) : size(theSize) {}
NormObjectSize(UINT16 msb, UINT32 lsb)
{
size = (Offset)lsb;
size |= (sizeof(Offset) > 4) ? (((Offset)msb) << 32): (Offset)0;
}
Offset GetOffset() const {return size;}
UINT16 MSB() const
{return ((sizeof(Offset) > 4) ? (UINT16)((size >> 32) & 0x0000ffff) : 0);}
UINT32 LSB() const
{return ((UINT32)(size & 0xffffffff));}
// Operators
bool operator==(const NormObjectSize& b) const
{return (b.size == size);}
NormObjectSize operator+(const NormObjectSize& b) const
{
NormObjectSize result(size);
result.size += b.size;
return result;
}
void operator+=(const NormObjectSize& b)
{size += b.size;}
NormObjectSize operator-(const NormObjectSize& b) const
{
NormObjectSize result(size);
result.size -= b.size;
return result;
}
void operator-=(const NormObjectSize& b)
{size -= b.size;}
void operator+=(Offset increment)
{size += increment;}
bool operator>(const NormObjectSize& b) const
{return (size > b.size);}
NormObjectSize operator*(const NormObjectSize& b) const
{
NormObjectSize result(size);
result.size *= b.size;
return result;
}
// Note: this is a "round-upwards" division operator
NormObjectSize operator/(const NormObjectSize& b) const
{
NormObjectSize result(size);
result.size /= b.size;
result.size = ((result.size * b.size) < size) ? result.size + 1 : result.size;
return result;
}
private:
Offset size;
}; // end class NormObjectSize
#ifndef _NORM_API
typedef unsigned long NormNodeId;
const NormNodeId NORM_NODE_NONE = 0x00000000;
const NormNodeId NORM_NODE_ANY = 0xffffffff;
#endif // !_NORM_API
class NormObjectId
{
public:
NormObjectId() {};
NormObjectId(UINT16 id) {value = id;}
NormObjectId(const NormObjectId& id) {value = id.value;}
operator UINT16() const {return value;}
INT16 operator-(const NormObjectId& id) const
{return ((INT16)(value - id.value));}
bool operator<(const NormObjectId& id) const
{return (((short)(value - id.value)) < 0);}
bool operator>(const NormObjectId& id) const
{return (((INT16)(value - id.value)) > 0);}
bool operator<=(const NormObjectId& id) const
{return ((value == id.value) || (*this<id));}
bool operator>=(const NormObjectId& id) const
{return ((value == id.value) || (*this>id));}
bool operator==(const NormObjectId& id) const
{return (value == id.value);}
bool operator!=(const NormObjectId& id) const
{return (value != id.value);}
NormObjectId& operator++(int) {value++; return *this;}
NormObjectId& operator--(int) {value--; return *this;}
private:
UINT16 value;
}; // end class NormObjectId
class NormBlockId
{
public:
NormBlockId() {};
NormBlockId(UINT32 id) {value = id;}
NormBlockId(const NormObjectId& id) {value = (UINT32)id;}
operator UINT32() const {return value;}
bool operator==(const NormBlockId& id) const
{return (value == (UINT32)id);}
bool operator!=(const NormBlockId& id) const
{return (value != (UINT32)id);}
INT32 operator-(const NormBlockId& id) const
{return ((INT32)value - id.value);}
bool operator<(const NormBlockId& id) const
{return (((INT32)(value-id.value)) < 0);}
bool operator>(const NormBlockId& id) const
{return (((INT32)(value-id.value)) > 0);}
NormBlockId& operator++(int ) {value++; return *this;}
private:
UINT32 value;
}; // end class NormBlockId
typedef UINT16 NormSymbolId;
typedef NormSymbolId NormSegmentId;
// Base class for NORM header extensions
class NormHeaderExtension
{
public:
enum Type
{
INVALID = 0,
FTI = 64, // FEC Object Transmission Information (FTI) extension
CC_FEEDBACK = 3, // NORM-CC Feedback extension
CC_RATE = 128 // NORM-CC Rate extension
};
NormHeaderExtension();
virtual ~NormHeaderExtension() {}
virtual void Init(UINT32* theBuffer)
{
buffer = theBuffer;
SetType(INVALID);
SetWords(0);
}
void SetType(Type type)
{((UINT8*)buffer)[TYPE_OFFSET] = (UINT8)type;}
void SetWords(UINT8 words)
{((UINT8*)buffer)[LENGTH_OFFSET] = words;}
void AttachBuffer(const UINT32* theBuffer) {buffer = (UINT32*)theBuffer;}
const UINT32* GetBuffer() {return buffer;}
Type GetType() const
{
return buffer ? (Type)(((UINT8*)buffer)[TYPE_OFFSET]) : INVALID;
}
UINT16 GetLength() const
{
return (buffer ?
((GetType() < 128) ?
((((UINT8*)buffer)[LENGTH_OFFSET]) << 2) : 4) :
0);
}
protected:
enum
{
TYPE_OFFSET = 0,
LENGTH_OFFSET = TYPE_OFFSET + 1
};
UINT32* buffer;
}; // end class NormHeaderExtension
class NormMsg
{
friend class NormMessageQueue;
public:
// (TBD) Use this "Type" enumeration instead of NormMsgType
enum Type
{
INVALID = 0,
INFO = 1,
DATA = 2,
CMD = 3,
NACK = 4,
ACK = 5,
REPORT = 6
};
enum {MAX_SIZE = 8192};
NormMsg();
// Message building routines
void SetVersion(UINT8 version)
{
((UINT8*)buffer)[VERSION_OFFSET] =
(((UINT8*)buffer)[VERSION_OFFSET] & 0x0f) | (version << 4);
}
void SetType(NormMsg::Type type)
{
((UINT8*)buffer)[TYPE_OFFSET] =
(((UINT8*)buffer)[VERSION_OFFSET] & 0xf0) | (type & 0x0f);
}
void SetSequence(UINT16 sequence)
{
((UINT16*)buffer)[SEQUENCE_OFFSET] = htons(sequence);
}
void SetSourceId(NormNodeId sourceId)
{
buffer[SOURCE_ID_OFFSET] = htonl(sourceId);
}
void SetDestination(const ProtoAddress& dst) {addr = dst;}
void AttachExtension(NormHeaderExtension& extension)
{
extension.Init(buffer+(header_length/4));
ExtendHeaderLength(extension.GetLength());
}
// Message processing routines
bool InitFromBuffer(UINT16 msgLength);
UINT8 GetVersion() const {return ((UINT8*)buffer)[VERSION_OFFSET];}
NormMsg::Type GetType() const {return (Type)(((UINT8*)buffer)[TYPE_OFFSET] & 0x0f);}
UINT16 GetHeaderLength() {return ((UINT8*)buffer)[HDR_LEN_OFFSET] << 2;}
UINT16 GetSequence() const
{
return (ntohs((((UINT16*)buffer)[SEQUENCE_OFFSET])));
}
NormNodeId GetSourceId() const
{
return (ntohl(buffer[SOURCE_ID_OFFSET]));
}
const ProtoAddress& GetDestination() const {return addr;}
const ProtoAddress& GetSource() const {return addr;}
const char* GetBuffer() {return ((char*)buffer);}
UINT16 GetLength() const {return length;}
// To retrieve any attached header extensions
bool HasExtensions() const {return (header_length > header_length_base);}
bool GetNextExtension(NormHeaderExtension& ext) const
{
const UINT32* currentBuffer = ext.GetBuffer();
UINT16 nextOffset =
(UINT16)(currentBuffer ? (currentBuffer - buffer + (ext.GetLength()/4)) :
(header_length_base/4));
bool result = (nextOffset < (header_length/4));
ext.AttachBuffer(result ? (buffer+nextOffset) : (UINT32*)NULL);
return result;
}
// For message reception and misc.
char* AccessBuffer() {return ((char*)buffer);}
ProtoAddress& AccessAddress() {return addr;}
NormMsg* GetNext() {return next;}
protected:
// Common message header offsets
// All of our offsets reflect their offset based on the field size!
// (So we can efficiently dereference msg fields with proper alignment)
enum
{
VERSION_OFFSET = 0,
TYPE_OFFSET = VERSION_OFFSET,
HDR_LEN_OFFSET = VERSION_OFFSET+1,
SEQUENCE_OFFSET = (HDR_LEN_OFFSET+1)/2,
SOURCE_ID_OFFSET = ((SEQUENCE_OFFSET*2)+2)/4,
MSG_OFFSET = (SOURCE_ID_OFFSET*4)+4
};
void SetBaseHeaderLength(UINT16 len)
{
((UINT8*)buffer)[HDR_LEN_OFFSET] = len >> 2;
length = header_length_base = header_length = len;
}
void ExtendHeaderLength(UINT16 len)
{
header_length += len;
length += len;
((UINT8*)buffer)[HDR_LEN_OFFSET] = header_length >> 2;
}
UINT32 buffer[MAX_SIZE / sizeof(UINT32)];
UINT16 length; // in bytes
UINT16 header_length;
UINT16 header_length_base;
ProtoAddress addr; // src or dst address
NormMsg* prev;
NormMsg* next;
}; // end class NormMsg
// "NormObjectMsg" is a base class for the similar "NormInfoMsg"
// and "NormDataMsg" types
class NormObjectMsg : public NormMsg
{
friend class NormMsg;
public:
enum Flag
{
FLAG_REPAIR = 0x01,
FLAG_EXPLICIT = 0x02,
FLAG_INFO = 0x04,
FLAG_UNRELIABLE = 0x08,
FLAG_FILE = 0x10,
FLAG_STREAM = 0x20
//FLAG_MSG_START = 0x40 deprecated
};
UINT16 GetInstanceId() const
{
return (ntohs(((UINT16*)buffer)[INSTANCE_ID_OFFSET]));
}
UINT8 GetGrtt() const {return ((UINT8*)buffer)[GRTT_OFFSET];}
UINT8 GetBackoffFactor() const {return ((((UINT8*)buffer)[GSIZE_OFFSET] >> 4) & 0x0f);}
UINT8 GetGroupSize() const {return (((UINT8*)buffer)[GSIZE_OFFSET] & 0x0f);}
bool FlagIsSet(NormObjectMsg::Flag flag) const
{return (0 != (flag & ((UINT8*)buffer)[FLAGS_OFFSET]));}
bool IsStream() const {return FlagIsSet(FLAG_STREAM);}
UINT8 GetFecId() const {return ((UINT8*)buffer)[FEC_ID_OFFSET];}
NormObjectId GetObjectId() const
{
return (ntohs(((UINT16*)buffer)[OBJ_ID_OFFSET]));
}
// Message building routines
void SetInstanceId(UINT16 instanceId)
{
((UINT16*)buffer)[INSTANCE_ID_OFFSET] = htons(instanceId);
}
void SetGrtt(UINT8 grtt) {((UINT8*)buffer)[GRTT_OFFSET] = grtt;}
void SetBackoffFactor(UINT8 backoff)
{
((UINT8*)buffer)[BACKOFF_OFFSET] = (((UINT8*)buffer)[GSIZE_OFFSET] & 0x0f) | (backoff << 4);
}
void SetGroupSize(UINT8 gsize)
{
((UINT8*)buffer)[GSIZE_OFFSET] = (((UINT8*)buffer)[GSIZE_OFFSET] & 0xf0) | gsize;
}
void ResetFlags() {((UINT8*)buffer)[FLAGS_OFFSET] = 0;}
void SetFlag(NormObjectMsg::Flag flag) {((UINT8*)buffer)[FLAGS_OFFSET] |= flag;}
void SetFecId(UINT8 fecId) {((UINT8*)buffer)[FEC_ID_OFFSET] = fecId;}
void SetObjectId(const NormObjectId& objectId)
{
((UINT16*)buffer)[OBJ_ID_OFFSET] = htons((UINT16)objectId);
}
protected:
enum
{
INSTANCE_ID_OFFSET = MSG_OFFSET/2,
GRTT_OFFSET = (INSTANCE_ID_OFFSET*2)+2,
BACKOFF_OFFSET = GRTT_OFFSET+1,
GSIZE_OFFSET = BACKOFF_OFFSET,
FLAGS_OFFSET = GSIZE_OFFSET+1,
FEC_ID_OFFSET = FLAGS_OFFSET+1,
OBJ_ID_OFFSET = (FEC_ID_OFFSET+1)/2,
OBJ_MSG_OFFSET = (OBJ_ID_OFFSET*2)+2
};
}; // end class NormObjectMsg
// This FEC Object Transmission Information assumes "fec_id" == 129
class NormFtiExtension : public NormHeaderExtension
{
public:
// To build the FTI Header Extension
// (TBD) allow for different "fec_id" types in the future
virtual void Init(UINT32* theBuffer)
{
AttachBuffer(theBuffer);
SetType(FTI);
SetWords(4);
}
void SetFecInstanceId(UINT16 instanceId)
{
((UINT16*)buffer)[FEC_INSTANCE_OFFSET] = htons(instanceId);
}
void SetFecMaxBlockLen(UINT16 ndata)
{
((UINT16*)buffer)[FEC_NDATA_OFFSET] = htons(ndata);
}
void SetFecNumParity(UINT16 nparity)
{
((UINT16*)buffer)[FEC_NPARITY_OFFSET] = htons(nparity);
}
void SetSegmentSize(UINT16 segmentSize)
{
((UINT16*)buffer)[SEG_SIZE_OFFSET] = htons(segmentSize);
}
void SetObjectSize(const NormObjectSize& objectSize)
{
((UINT16*)buffer)[OBJ_SIZE_MSB_OFFSET] = htons(objectSize.MSB());
buffer[OBJ_SIZE_LSB_OFFSET] = htonl(objectSize.LSB());
}
// FTI Extension parsing methods
UINT16 GetFecInstanceId() const
{
return (ntohs(((UINT16*)buffer)[FEC_INSTANCE_OFFSET]));
}
UINT16 GetFecMaxBlockLen() const
{
return (ntohs(((UINT16*)buffer)[FEC_NDATA_OFFSET]));
}
UINT16 GetFecNumParity() const
{
return (ntohs(((UINT16*)buffer)[FEC_NPARITY_OFFSET]));
}
UINT16 GetSegmentSize() const
{
return (ntohs(((UINT16*)buffer)[SEG_SIZE_OFFSET]));
}
NormObjectSize GetObjectSize() const
{
return NormObjectSize(ntohs(((UINT16*)buffer)[OBJ_SIZE_MSB_OFFSET]),
ntohl(buffer[OBJ_SIZE_LSB_OFFSET]));
}
private:
enum
{
OBJ_SIZE_MSB_OFFSET = (LENGTH_OFFSET + 1)/2,
OBJ_SIZE_LSB_OFFSET = ((OBJ_SIZE_MSB_OFFSET*2)+2)/4,
FEC_INSTANCE_OFFSET = ((OBJ_SIZE_LSB_OFFSET*4)+4)/2,
SEG_SIZE_OFFSET = ((FEC_INSTANCE_OFFSET*2)+2)/2,
FEC_NDATA_OFFSET = ((SEG_SIZE_OFFSET*2)+2)/2,
FEC_NPARITY_OFFSET = ((FEC_NDATA_OFFSET*2)+2)/2
};
}; // end class NormFtiExtension
class NormInfoMsg : public NormObjectMsg
{
public:
void Init()
{
SetType(INFO);
SetBaseHeaderLength(INFO_HEADER_LEN);
// Default "fec_id" = 129
SetFecId(129);
ResetFlags();
}
UINT16 GetInfoLen() const {return (length - header_length);}
const char* GetInfo() const {return (((char*)buffer) + header_length);}
// Note: apply any header extensions first
void SetInfo(const char* data, UINT16 size)
{
memcpy(((char*)buffer)+header_length, data, size);
length = size + header_length;
}
private:
enum {INFO_HEADER_LEN = OBJ_MSG_OFFSET};
}; // end class NormInfoMsg
class NormDataMsg : public NormObjectMsg
{
public:
void Init()
{
SetType(DATA);
// Default "fec_id" = 129 w/ "fec_payload_id" length = 8 bytes
SetBaseHeaderLength(DATA_HEADER_LEN);
SetFecId(129);
ResetFlags();
}
// Message building methods (in addition to NormObjectMsg fields)
void SetFecBlockId(const NormBlockId& blockId)
{
buffer[BLOCK_ID_OFFSET] = htonl((UINT32)blockId);
}
void SetFecBlockLen(UINT16 blockLen)
{
((UINT16*)buffer)[BLOCK_LEN_OFFSET] = htons(blockLen);
}
void SetFecSymbolId(UINT16 symbolId)
{
((UINT16*)buffer)[SYMBOL_ID_OFFSET] = htons(symbolId);
}
// Two ways to set payload content:
// 1) Directly access payload to copy segment, then set data message length
// (Note NORM_STREAM_OBJECT segments must already include "payload_len"
// and "payload_offset" with the "payload_data"
char* AccessPayload() {return (((char*)buffer)+header_length);}
// For NORM_STREAM_OBJECT segments, "dataLength" must include the PAYLOAD_HEADER_LENGTH
void SetPayloadLength(UINT16 payloadLength)
{
length = header_length + payloadLength;
}
// Set "payload" directly (useful for FEC parity segments)
void SetPayload(char* payload, UINT16 payloadLength)
{
memcpy(((char*)buffer)+header_length, payload, payloadLength);
length = header_length + payloadLength;
}
// AccessPayloadData() (useful for setting ZERO padding)
char* AccessPayloadData()
{
UINT16 payloadIndex = IsStream() ? header_length+PAYLOAD_DATA_OFFSET : header_length;
return (((char*)buffer)+payloadIndex);
}
// Message processing methods
NormBlockId GetFecBlockId() const
{return (ntohl(buffer[BLOCK_ID_OFFSET]));}
UINT16 GetFecBlockLen() const
{return (ntohs(((UINT16*)buffer)[BLOCK_LEN_OFFSET]));}
UINT16 GetFecSymbolId() const
{return (ntohs(((UINT16*)buffer)[SYMBOL_ID_OFFSET]));}
bool IsData() const
{return (GetFecSymbolId() < GetFecBlockLen());}
// Note: For NORM_OBJECT_STREAM, "payload" includes "payload_reserved",
// "payload_len", "payload_offset", and "payload_data" fields
// For NORM_OBJECT_FILE and NORM_OBJECT_DATA, "payload" includes
// "payload_data" only
const char* GetPayload() const {return (((char*)buffer)+header_length);}
UINT16 GetPayloadLength() const {return (length - header_length);}
const char* GetPayloadData() const
{
UINT16 dataIndex = IsStream() ? header_length+PAYLOAD_DATA_OFFSET : header_length;
return (((char*)buffer)+dataIndex);
}
UINT16 GetPayloadDataLength() const
{
UINT16 dataIndex = IsStream() ? header_length+PAYLOAD_DATA_OFFSET : header_length;
return (length - dataIndex);
}
// These routines are only applicable to messages containing NORM_OBJECT_STREAM content
// Some static helper routines for reading/writing embedded payload length/offsets
static UINT16 GetStreamPayloadHeaderLength() {return (PAYLOAD_DATA_OFFSET);}
/* Payload flags have deprecated in the revised NORM spec
enum PayloadFlag
{
FLAG_STREAM_END = 0x01,
FLAG_MSG_START = 0x04
};
static bool StreamPayloadFlagIsSet(const char* payload,
PayloadFlag flag)
{
return (0 != (flag & ((UINT8*)payload)[PAYLOAD_FLAGS_OFFSET]));
}
static void ResetStreamPayloadFlags(char* payload)
{
((UINT8*)payload)[PAYLOAD_FLAGS_OFFSET] = 0;
}
static void SetStreamPayloadFlag(char* payload, PayloadFlag flag)
{
((UINT8*)payload)[PAYLOAD_FLAGS_OFFSET] |= flag;
}*/
static void WriteStreamPayloadMsgStart(char* payload, UINT16 msgStartOffset)
{
UINT16 temp16 = htons(msgStartOffset);
memcpy(payload+PAYLOAD_MSG_START_OFFSET, &temp16, 2);
}
static void WriteStreamPayloadLength(char* payload, UINT16 len)
{
UINT16 temp16 = htons(len);
memcpy(payload+PAYLOAD_LENGTH_OFFSET, &temp16, 2);
}
static void WriteStreamPayloadOffset(char* payload, UINT32 offset)
{
UINT32 temp32 = htonl(offset);
memcpy(payload+PAYLOAD_OFFSET_OFFSET, &temp32, 4);
}
static UINT16 ReadStreamPayloadMsgStart(const char* payload)
{
UINT16 temp16;
memcpy(&temp16, payload+PAYLOAD_MSG_START_OFFSET, 2);
return (ntohs(temp16));
}
static UINT16 ReadStreamPayloadLength(const char* payload)
{
UINT16 temp16;
memcpy(&temp16, payload+PAYLOAD_LENGTH_OFFSET, 2);
return (ntohs(temp16));
}
static UINT32 ReadStreamPayloadOffset(const char* payload)
{
UINT32 temp32;
memcpy(&temp32, payload+PAYLOAD_OFFSET_OFFSET, 4);
return (ntohl(temp32));
}
private:
enum
{
BLOCK_ID_OFFSET = OBJ_MSG_OFFSET/4,
BLOCK_LEN_OFFSET = ((BLOCK_ID_OFFSET*4)+4)/2,
SYMBOL_ID_OFFSET = ((BLOCK_LEN_OFFSET*2)+2)/2,
DATA_HEADER_LEN = (SYMBOL_ID_OFFSET*2)+2
};
enum
{
//PAYLOAD_FLAGS_OFFSET = 0, // deprecated
PAYLOAD_MSG_START_OFFSET = 0,
PAYLOAD_LENGTH_OFFSET = PAYLOAD_MSG_START_OFFSET+2,
PAYLOAD_OFFSET_OFFSET = PAYLOAD_LENGTH_OFFSET+2,
PAYLOAD_DATA_OFFSET = PAYLOAD_OFFSET_OFFSET+4
};
}; // end class NormDataMsg
class NormCmdMsg : public NormMsg
{
public:
enum Flavor
{
INVALID = 0,
FLUSH = 1,
EOT = 2,
SQUELCH = 3,
CC = 4,
REPAIR_ADV = 5,
ACK_REQ = 6,
APPLICATION = 7
};
// Message building
void SetInstanceId(UINT16 instanceId)
{
((UINT16*)buffer)[INSTANCE_ID_OFFSET] = htons(instanceId);
}
void SetGrtt(UINT8 quantizedGrtt)
{((UINT8*)buffer)[GRTT_OFFSET] = quantizedGrtt;}
void SetBackoffFactor(UINT8 backoff)
{
((UINT8*)buffer)[BACKOFF_OFFSET] = (((UINT8*)buffer)[GSIZE_OFFSET] & 0x0f) | (backoff << 4);
}
void SetGroupSize(UINT8 gsize)
{
((UINT8*)buffer)[GSIZE_OFFSET] = (((UINT8*)buffer)[GSIZE_OFFSET] & 0xf0) | gsize;
}
void SetFlavor(NormCmdMsg::Flavor flavor)
{((UINT8*)buffer)[FLAVOR_OFFSET] = (UINT8)flavor;}
// Message processing
UINT16 GetInstanceId() const {return (ntohs(((UINT16*)buffer)[INSTANCE_ID_OFFSET]));}
UINT8 GetGrtt() const {return ((UINT8*)buffer)[GRTT_OFFSET];}
UINT8 GetBackoffFactor() const {return ((((UINT8*)buffer)[GSIZE_OFFSET] >> 4) & 0x0f);}
UINT8 GetGroupSize() const {return (((UINT8*)buffer)[GSIZE_OFFSET] & 0x0f);}
NormCmdMsg::Flavor GetFlavor() const {return (Flavor)((UINT8*)buffer)[FLAVOR_OFFSET];}
protected:
friend class NormMsg;
enum
{
INSTANCE_ID_OFFSET = MSG_OFFSET/2,
GRTT_OFFSET = (INSTANCE_ID_OFFSET*2)+2,
BACKOFF_OFFSET = GRTT_OFFSET + 1,
GSIZE_OFFSET = BACKOFF_OFFSET,
FLAVOR_OFFSET = GSIZE_OFFSET + 1
};
}; // end class NormCmdMsg
class NormCmdFlushMsg : public NormCmdMsg
{
friend class NormMsg;
public:
void Init()
{
SetType(CMD);
SetFlavor(FLUSH);
SetBaseHeaderLength(FLUSH_HEADER_LEN);
SetFecId(129); // default "fec_id"
}
void SetFecId(UINT8 fecId) {((UINT8*)buffer)[FEC_ID_OFFSET] = fecId;}
void SetObjectId(const NormObjectId& objectId)
{
((UINT16*)buffer)[OBJ_ID_OFFSET] = htons((UINT16)objectId);
}
// "fec_payload_id" fields, assuming "fec_id" = 129
void SetFecBlockId(const NormBlockId& blockId)
{
buffer[BLOCK_ID_OFFSET] = htonl((UINT32)blockId);
}
void SetFecBlockLen(UINT16 blockLen)
{
((UINT16*)buffer)[BLOCK_LEN_OFFSET] = htons((UINT16)blockLen);
}
void SetFecSymbolId(UINT16 symbolId)
{
((UINT16*)buffer)[SYMBOL_ID_OFFSET] = htons((UINT16)symbolId);
}
void ResetAckingNodeList() {length = header_length;}
bool AppendAckingNode(NormNodeId nodeId, UINT16 segmentSize)
{
if ((length-header_length+ 4) > segmentSize) return false;
buffer[length/4] = htonl((UINT32)nodeId);
length += 4;
return true;
}
// Message processing
UINT8 GetFecId() {return ((UINT8*)buffer)[FEC_ID_OFFSET];}
NormObjectId GetObjectId() const
{
return (ntohs(((UINT16*)buffer)[OBJ_ID_OFFSET]));
}
NormBlockId GetFecBlockId() const
{
return (ntohl(buffer[BLOCK_ID_OFFSET]));
}
UINT16 GetFecBlockLen() const
{
return (ntohs(((UINT16*)buffer)[BLOCK_LEN_OFFSET]));
}
UINT16 GetFecSymbolId() const
{
return (ntohs(((UINT16*)buffer)[SYMBOL_ID_OFFSET]));
}
UINT16 GetAckingNodeCount() const {return ((length - header_length) >> 2);}
const UINT32* GetAckingNodeList() const {return (buffer+(header_length/4));}
NormNodeId GetAckingNodeId(UINT16 index) const
{
return (ntohl(buffer[(header_length/4)+index]));
}
private:
enum
{
FEC_ID_OFFSET = FLAVOR_OFFSET + 1,
OBJ_ID_OFFSET = (FEC_ID_OFFSET + 1)/2,
BLOCK_ID_OFFSET = ((OBJ_ID_OFFSET*2)+2)/4,
BLOCK_LEN_OFFSET = ((BLOCK_ID_OFFSET*4)+4)/2,
SYMBOL_ID_OFFSET = ((BLOCK_LEN_OFFSET*2)+2)/2,
FLUSH_HEADER_LEN = (SYMBOL_ID_OFFSET*2)+2
};
}; // end class NormCmdFlushMsg
class NormCmdEotMsg : public NormCmdMsg
{
public:
void Init()
{
SetType(CMD);
SetFlavor(EOT);
SetBaseHeaderLength(EOT_HEADER_LEN);
memset(((char*)buffer)+RESERVED_OFFSET, 0, 3);
}
private:
enum
{
RESERVED_OFFSET = FLAVOR_OFFSET + 1,
EOT_HEADER_LEN = RESERVED_OFFSET + 3
};
}; // end class NormCmdEotMsg
class NormCmdSquelchMsg : public NormCmdMsg
{
public:
// Message building
void Init()
{
SetType(CMD);
SetFlavor(SQUELCH);
SetBaseHeaderLength(SQUELCH_HEADER_LEN);
SetFecId(129); // default "fec_id"
}
void SetFecId(UINT8 fecId)
{((UINT8*)buffer)[FEC_ID_OFFSET] = fecId;}
void SetObjectId(const NormObjectId& objectId)
{
((UINT16*)buffer)[OBJ_ID_OFFSET] = htons((UINT16)objectId);
}
// "fec_payload_id" fields, assuming "fec_id" = 129
void SetFecBlockId(const NormBlockId& blockId)
{
buffer[BLOCK_ID_OFFSET] = htonl((UINT32)blockId);
}
void SetFecBlockLen(UINT16 blockLen)
{
((UINT16*)buffer)[BLOCK_LEN_OFFSET] = htons((UINT16)blockLen);
}
void SetFecSymbolId(UINT16 symbolId)
{
((UINT16*)buffer)[SYMBOL_ID_OFFSET] = htons((UINT16)symbolId);
}
void ResetInvalidObjectList() {length = header_length;}
bool AppendInvalidObject(NormObjectId objectId, UINT16 segmentSize)
{
if ((length-header_length+2) > segmentSize) return false;
((UINT16*)buffer)[length/2] = htons((UINT16)objectId);
length += 2;
return true;
}
// Message processing
UINT8 GetFecId() {return ((UINT8*)buffer)[FEC_ID_OFFSET];}
NormObjectId GetObjectId() const
{
return (ntohs(((UINT16*)buffer)[OBJ_ID_OFFSET]));
}
NormBlockId GetFecBlockId() const
{
return (ntohl(buffer[BLOCK_ID_OFFSET]));
}
UINT16 GetFecBlockLen() const
{
return (ntohs(((UINT16*)buffer)[BLOCK_LEN_OFFSET]));
}
UINT16 GetFecSymbolId() const
{
return (ntohs(((UINT16*)buffer)[SYMBOL_ID_OFFSET]));
}
// Use these to parse invalid object list
UINT16 GetInvalidObjectCount() const {return ((length - header_length) >> 1);}
UINT16* GetInvalidObjectList() const {return (UINT16*)(buffer+header_length);}
NormObjectId GetInvalidObjectId(UINT16 index) const
{
return (ntohs(((UINT16*)buffer)[(header_length/2)+index]));
}
private:
enum
{
FEC_ID_OFFSET = FLAVOR_OFFSET + 1,
OBJ_ID_OFFSET = (FEC_ID_OFFSET + 1)/2,
BLOCK_ID_OFFSET = ((OBJ_ID_OFFSET*2)+2)/4,
BLOCK_LEN_OFFSET = ((BLOCK_ID_OFFSET*4)+4)/2,
SYMBOL_ID_OFFSET = ((BLOCK_LEN_OFFSET*2)+2)/2,
SQUELCH_HEADER_LEN = (SYMBOL_ID_OFFSET*2)+2
};
}; // end class NormCmdSquelchMsg
// These flag values are used "cc_flags" field of NORM_CMD(CC) CC_NODE_LIST
// items and NORM_NACK and NORM_ACK messages
class NormCC
{
public:
enum Flag
{
CLR = 0x01,
PLR = 0x02,
RTT = 0x04,
START = 0x08,
LEAVE = 0x10
};
}; // end class NormCC
class NormCmdCCMsg : public NormCmdMsg
{
public:
void Init()
{
SetType(CMD);
SetFlavor(CC);
SetBaseHeaderLength(CC_HEADER_LEN);
((UINT8*)buffer)[RESERVED_OFFSET] = 0;
}
void SetCCSequence(UINT16 ccSequence)
{
((UINT16*)buffer)[CC_SEQUENCE_OFFSET] = htons(ccSequence);
}
void SetSendTime(const struct timeval& sendTime)
{
buffer[SEND_TIME_SEC_OFFSET] = htonl(sendTime.tv_sec);
buffer[SEND_TIME_USEC_OFFSET] = htonl(sendTime.tv_usec);
}
UINT16 GetCCSequence() const
{
return (ntohs(((UINT16*)buffer)[CC_SEQUENCE_OFFSET]));
}
void GetSendTime(struct timeval& sendTime) const
{
sendTime.tv_sec = ntohl(buffer[SEND_TIME_SEC_OFFSET]);
sendTime.tv_usec = ntohl(buffer[SEND_TIME_USEC_OFFSET]);
}
bool AppendCCNode(UINT16 segMax, NormNodeId nodeId, UINT8 flags,
UINT8 rtt, UINT16 rate)
{
if ((length-header_length+CC_ITEM_SIZE)> segMax) return false;
UINT32* ptr = buffer + length/4;
ptr[CC_NODE_ID_OFFSET] = htonl(nodeId);
((UINT8*)ptr)[CC_FLAGS_OFFSET] = flags;
((UINT8*)ptr)[CC_RTT_OFFSET] = rtt;
((UINT16*)ptr)[CC_RATE_OFFSET] = htons(rate);
length += CC_ITEM_SIZE;
return true;
}
bool GetCCNode(NormNodeId nodeId, UINT8& flags, UINT8& rtt, UINT16& rate) const;
class Iterator;
friend class Iterator;
class Iterator
{
public:
Iterator(const NormCmdCCMsg& msg);
void Reset() {offset = 0;}
bool GetNextNode(NormNodeId& nodeId, UINT8& flags, UINT8& rtt, UINT16& rate);
private:
const NormCmdCCMsg& cc_cmd;
UINT16 offset;
};
private:
enum
{
RESERVED_OFFSET = FLAVOR_OFFSET + 1,
CC_SEQUENCE_OFFSET = (RESERVED_OFFSET + 1)/2,
SEND_TIME_SEC_OFFSET = ((CC_SEQUENCE_OFFSET*2)+2)/4,
SEND_TIME_USEC_OFFSET = ((SEND_TIME_SEC_OFFSET*4)+4)/4,
CC_HEADER_LEN = (SEND_TIME_USEC_OFFSET*4)+4
};
enum
{
CC_NODE_ID_OFFSET = 0,
CC_FLAGS_OFFSET = CC_NODE_ID_OFFSET + 4,
CC_RTT_OFFSET = CC_FLAGS_OFFSET + 1,
CC_RATE_OFFSET = (CC_RTT_OFFSET + 1)/2,
CC_ITEM_SIZE = (CC_RATE_OFFSET*2)+2
};
}; // end NormCmdCCMsg
class NormCCRateExtension : public NormHeaderExtension
{
public:
virtual void Init(UINT32* theBuffer)
{
AttachBuffer(theBuffer);
SetType(CC_RATE);
((UINT8*)buffer)[RESERVED_OFFSET] = 0;
}
void SetSendRate(UINT16 sendRate)
{((UINT16*)buffer)[SEND_RATE_OFFSET] = htons(sendRate);}
UINT16 GetSendRate()
{return (ntohs(((UINT16*)buffer)[SEND_RATE_OFFSET]));}
private:
enum
{
RESERVED_OFFSET = TYPE_OFFSET + 1,
SEND_RATE_OFFSET = (RESERVED_OFFSET + 1)/2
};
}; // end class NormCCRateExtension
// This implementation currently assumes "fec_id"= 129
class NormRepairRequest
{
public:
class Iterator;
friend class NormRepairRequest::Iterator;
enum Form
{
INVALID = 0,
ITEMS = 1,
RANGES = 2,
ERASURES = 3
};
enum Flag
{
SEGMENT = 0x01,
BLOCK = 0x02,
INFO = 0x04,
OBJECT = 0x08
};
// Construction
NormRepairRequest();
void Init(UINT32* bufferPtr, UINT16 bufferLen)
{
buffer = bufferPtr;
buffer_len = bufferLen;
length = 0;
}
// (TBD) these could be an enumeration for optimization
static UINT16 RepairItemLength() {return 12;}
static UINT16 RepairRangeLength() {return 24;}
static UINT16 ErasureItemLength() {return 12;}
// Repair request building
void SetForm(NormRepairRequest::Form theForm) {form = theForm;}
void ResetFlags() {flags = 0;}
void SetFlag(NormRepairRequest::Flag theFlag) {flags |= theFlag;}
void ClearFlag(NormRepairRequest::Flag theFlag) {flags &= ~theFlag;}
// Returns length (each repair item requires 8 bytes of space)
bool AppendRepairItem(const NormObjectId& objectId,
const NormBlockId& blockId,
UINT16 blockLen,
UINT16 symbolId);
bool AppendRepairRange(const NormObjectId& startObjectId,
const NormBlockId& startBlockId,
UINT16 startBlockLen,
UINT16 startSymbolId,
const NormObjectId& endObjectId,
const NormBlockId& endBlockId,
UINT16 endBlockLen,
UINT16 endSymbolId);
bool AppendErasureCount(const NormObjectId& objectId,
const NormBlockId& blockId,
UINT16 blockLen,
UINT16 erasureCount);
UINT16 Pack();
// Repair request processing
UINT16 Unpack(const UINT32* bufferPtr, UINT16 bufferLen);
NormRepairRequest::Form GetForm() const {return form;}
bool FlagIsSet(NormRepairRequest::Flag theFlag) const
{return (0 != (theFlag & flags));}
UINT16 GetLength() const {return length;}
class Iterator
{
public:
Iterator(NormRepairRequest& theRequest);
void Reset() {offset = 0;}
bool NextRepairItem(NormObjectId* objectId,
NormBlockId* blockId,
UINT16* blockLen,
UINT16* symbolId);
private:
NormRepairRequest& request;
UINT16 offset;
}; // end class NormRepairRequest::Iterator
private:
bool RetrieveRepairItem(UINT16 offset,
NormObjectId* objectId,
NormBlockId* blockId,
UINT16* blockLen,
UINT16* symbolId) const;
enum
{
FORM_OFFSET = 0,
FLAGS_OFFSET = FORM_OFFSET + 1,
LENGTH_OFFSET = (FLAGS_OFFSET + 1)/2,
ITEM_LIST_OFFSET = (LENGTH_OFFSET*2)+2
};
// These are the offsets for "fec_id" = 129 NormRepairRequest items
enum
{
FEC_ID_OFFSET = 0,
RESERVED_OFFSET = FEC_ID_OFFSET + 1,
OBJ_ID_OFFSET = (RESERVED_OFFSET + 1)/2,
BLOCK_ID_OFFSET = ((OBJ_ID_OFFSET*2)+2)/4,
BLOCK_LEN_OFFSET = ((BLOCK_ID_OFFSET*4)+4)/2,
SYMBOL_ID_OFFSET = ((BLOCK_LEN_OFFSET*2)+2)/2
};
Form form;
int flags;
UINT16 length; // length of repair items
UINT32* buffer;
UINT16 buffer_len; // in bytes
}; // end class NormRepairRequest
class NormCmdRepairAdvMsg : public NormCmdMsg
{
public:
enum Flag {NORM_REPAIR_ADV_FLAG_LIMIT = 0x01};
void Init()
{
SetType(CMD);
SetFlavor(REPAIR_ADV);
SetBaseHeaderLength(REPAIR_ADV_HEADER_LEN);
ResetFlags();
((UINT16*)buffer)[RESERVED_OFFSET] = 0;
}
// Message building
void ResetFlags() {((UINT8*)buffer)[FLAGS_OFFSET] = 0;}
void SetFlag(NormCmdRepairAdvMsg::Flag flag)
{((UINT8*)buffer)[FLAGS_OFFSET] |= (UINT8)flag;}
void AttachRepairRequest(NormRepairRequest& request,
UINT16 segmentMax)
{
int buflen = segmentMax - (length - header_length);
buflen = (buflen>0) ? buflen : 0;
request.Init(buffer+length/4, buflen);
}
UINT16 PackRepairRequest(NormRepairRequest& request)
{
UINT16 requestLength = request.Pack();
length += requestLength;
return requestLength;
}
// Message processing
bool FlagIsSet(NormCmdRepairAdvMsg::Flag flag) const
{
return (0 != ((UINT8)flag | ((UINT8*)buffer)[FLAGS_OFFSET]));
}
//char* AccessRepairContent() {return (buffer + header_length);}
const UINT32* GetRepairContent() const {return (buffer + header_length/4);}
UINT16 GetRepairContentLength() const {return (length - header_length);}
private:
enum
{
FLAGS_OFFSET = FLAVOR_OFFSET + 1,
RESERVED_OFFSET = FLAGS_OFFSET + 1,
REPAIR_ADV_HEADER_LEN = RESERVED_OFFSET + 2
};
}; // end class NormCmdRepairAdvMsg
class NormCCFeedbackExtension : public NormHeaderExtension
{
public:
virtual void Init(UINT32* theBuffer)
{
AttachBuffer(theBuffer);
SetType(CC_FEEDBACK);
SetWords(3);
((UINT8*)buffer)[CC_FLAGS_OFFSET] = 0;
((UINT16*)buffer)[CC_RESERVED_OFFSET] = 0;
}
void SetCCSequence(UINT16 ccSequence)
{
((UINT16*)buffer)[CC_SEQUENCE_OFFSET] = htons(ccSequence);
}
void ResetCCFlags()
{((UINT8*)buffer)[CC_FLAGS_OFFSET] = 0;}
void SetCCFlag(NormCC::Flag flag)
{((UINT8*)buffer)[CC_FLAGS_OFFSET] |= (UINT8)flag;}
void SetCCRtt(UINT8 ccRtt)
{((UINT8*)buffer)[CC_RTT_OFFSET] = ccRtt;}
void SetCCLoss(UINT16 ccLoss)
{((UINT16*)buffer)[CC_LOSS_OFFSET] = htons(ccLoss);}
void SetCCRate(UINT16 ccRate)
{((UINT16*)buffer)[CC_RATE_OFFSET] = htons(ccRate);}
UINT16 GetCCSequence() const
{return (ntohs(((UINT16*)buffer)[CC_SEQUENCE_OFFSET]));}
UINT8 GetCCFlags()
{return ((UINT8*)buffer)[CC_FLAGS_OFFSET];}
bool CCFlagIsSet(NormCC::Flag flag) const
{
return (0 != ((UINT8)flag & ((UINT8*)buffer)[CC_FLAGS_OFFSET]));
}
UINT8 GetCCRtt()
{return ((UINT8*)buffer)[CC_RTT_OFFSET];}
UINT16 GetCCLoss()
{return (ntohs(((UINT16*)buffer)[CC_LOSS_OFFSET]));}
UINT16 GetCCRate()
{return (ntohs(((UINT16*)buffer)[CC_RATE_OFFSET]));}
private:
enum
{
CC_SEQUENCE_OFFSET = (LENGTH_OFFSET+1)/2,
CC_FLAGS_OFFSET = (CC_SEQUENCE_OFFSET*2)+2,
CC_RTT_OFFSET = CC_FLAGS_OFFSET + 1,
CC_LOSS_OFFSET = (CC_RTT_OFFSET + 1)/2,
CC_RATE_OFFSET = ((CC_LOSS_OFFSET*2)+2)/2,
CC_RESERVED_OFFSET = ((CC_RATE_OFFSET*2)+2)/2
};
}; // end class NormCCFeedbackExtension
// Note: Support for application-defined AckFlavors (32-255)
// may be provided, but 0-31 are reserved values
class NormAck
{
public:
enum Type
{
INVALID = 0,
CC = 1,
FLUSH = 2,
APP_BASE = 16
};
};
class NormCmdAckReqMsg : public NormCmdMsg
{
public:
void Init()
{
SetType(CMD);
SetFlavor(ACK_REQ);
SetBaseHeaderLength(ACK_REQ_HEADER_LEN);
((UINT8*)buffer)[RESERVED_OFFSET] = 0;
}
// Message building
void SetAckType(NormAck::Type ackType) {((UINT8*)buffer)[ACK_TYPE_OFFSET] = (UINT8)ackType;}
void SetAckId(UINT8 ackId) {((UINT8*)buffer)[ACK_ID_OFFSET] = ackId;}
void ResetAckingNodeList() {length = header_length;}
bool AppendAckingNode(NormNodeId nodeId, UINT16 segmentSize)
{
if ((length - header_length + 4) > segmentSize) return false;
buffer[length/4] = htonl(nodeId);
length += 4;
return true;
}
// Message processing
NormAck::Type GetAckType() const
{return (NormAck::Type)(((UINT8*)buffer)[ACK_TYPE_OFFSET]);}
UINT8 GetAckId() const
{return ((UINT8*)buffer)[ACK_ID_OFFSET];}
UINT16 GetAckingNodeCount() const
{return ((length - header_length) >> 2);}
NormNodeId GetAckingNodeId(UINT16 index) const
{
return (ntohl(buffer[(header_length/4)+index]));
}
private:
enum
{
RESERVED_OFFSET = FLAVOR_OFFSET + 1,
ACK_TYPE_OFFSET = RESERVED_OFFSET + 1,
ACK_ID_OFFSET = ACK_TYPE_OFFSET + 1,
ACK_REQ_HEADER_LEN = ACK_ID_OFFSET + 1
};
}; // end class NormCmdAckReqMsg
class NormCmdApplicationMsg : public NormCmdMsg
{
public:
void Init()
{
SetType(CMD);
SetFlavor(APPLICATION);
SetBaseHeaderLength(APPLICATION_HEADER_LEN);
memset(((UINT8*)buffer)+RESERVED_OFFSET, 0, 3);
}
bool SetContent(const char* content, UINT16 contentLen, UINT16 segmentSize)
{
UINT16 len = MIN(contentLen, segmentSize);
memcpy(((char*)buffer)+header_length, content, len);
return (contentLen <= segmentSize);
}
UINT16 GetContentLength() {return (length - header_length);}
const char* GetContent() {return (((char*)buffer)+header_length);}
private:
enum
{
RESERVED_OFFSET = FLAVOR_OFFSET + 1,
APPLICATION_HEADER_LEN = RESERVED_OFFSET + 3
};
}; // end class NormCmdApplicationMsg
// Receiver Messages
class NormNackMsg : public NormMsg
{
public:
void Init()
{
SetType(NACK);
SetBaseHeaderLength(NACK_HEADER_LEN);
}
// Message building
void SetServerId(NormNodeId serverId)
{
buffer[SERVER_ID_OFFSET] = htonl(serverId);
}
void SetInstanceId(UINT16 instanceId)
{
((UINT16*)buffer)[INSTANCE_ID_OFFSET] = htons(instanceId);
}
void SetGrttResponse(const struct timeval& grttResponse)
{
buffer[GRTT_RESPONSE_SEC_OFFSET] = htonl(grttResponse.tv_sec);
buffer[GRTT_RESPONSE_USEC_OFFSET] = htonl(grttResponse.tv_usec);
}
void AttachRepairRequest(NormRepairRequest& request,
UINT16 segmentMax)
{
int buflen = segmentMax - (length - header_length);
buflen = (buflen>0) ? buflen : 0;
request.Init(buffer+length/4, buflen);
}
UINT16 PackRepairRequest(NormRepairRequest& request)
{
UINT16 requestLength = request.Pack();
length += requestLength;
return requestLength;
}
// Message processing
NormNodeId GetServerId() const
{
return (ntohl(buffer[SERVER_ID_OFFSET]));
}
UINT16 GetInstanceId() const
{
return (ntohs(((UINT16*)buffer)[INSTANCE_ID_OFFSET]));
}
void GetGrttResponse(struct timeval& grttResponse) const
{
grttResponse.tv_sec = ntohl(buffer[GRTT_RESPONSE_SEC_OFFSET]);
grttResponse.tv_usec = ntohl(buffer[GRTT_RESPONSE_USEC_OFFSET]);
}
//char* AccessRepairContent() {return (buffer + header_length);}
const UINT32* GetRepairContent() const {return (buffer + header_length/4);}
UINT16 GetRepairContentLength() const
{return ((length > header_length) ? length - header_length : 0);}
UINT16 UnpackRepairRequest(NormRepairRequest& request,
UINT16 requestOffset)
{
int buflen = length - header_length - requestOffset;
buflen = (buflen > 0) ? buflen : 0;
return request.Unpack(buffer+(header_length+requestOffset)/4, buflen);
}
private:
enum
{
SERVER_ID_OFFSET = MSG_OFFSET/4,
INSTANCE_ID_OFFSET = ((SERVER_ID_OFFSET*4)+4)/2,
RESERVED_OFFSET = ((INSTANCE_ID_OFFSET*2)+2)/2,
GRTT_RESPONSE_SEC_OFFSET = ((RESERVED_OFFSET*2)+2)/4,
GRTT_RESPONSE_USEC_OFFSET = ((GRTT_RESPONSE_SEC_OFFSET*4)+4)/4,
NACK_HEADER_LEN = (GRTT_RESPONSE_USEC_OFFSET*4)+4
};
}; // end class NormNackMsg
class NormAckMsg : public NormMsg
{
public:
// Message building
void Init()
{
SetType(ACK);
SetBaseHeaderLength(ACK_HEADER_LEN);
SetAckType(NormAck::INVALID);
}
void SetServerId(NormNodeId serverId)
{
buffer[SERVER_ID_OFFSET] = htonl(serverId);
}
void SetInstanceId(UINT16 instanceId)
{
((UINT16*)buffer)[INSTANCE_ID_OFFSET] = htons(instanceId);
}
void SetAckType(NormAck::Type ackType) {((UINT8*)buffer)[ACK_TYPE_OFFSET] = (UINT8)ackType;}
void SetAckId(UINT8 ackId) {((UINT8*)buffer)[ACK_ID_OFFSET] = ackId;}
void SetGrttResponse(const struct timeval& grttResponse)
{
buffer[GRTT_RESPONSE_SEC_OFFSET] = htonl(grttResponse.tv_sec);
buffer[GRTT_RESPONSE_USEC_OFFSET] = htonl(grttResponse.tv_usec);
}
bool SetAckPayload(const char* payload, UINT16 payloadLen, UINT16 segmentSize)
{
UINT16 len = MIN(payloadLen, segmentSize);
memcpy(((char*)buffer)+header_length, payload, len);
length += len;
return (payloadLen <= segmentSize);
}
// Message processing
NormNodeId GetServerId() const
{
return (ntohl(buffer[SERVER_ID_OFFSET]));
}
UINT16 GetInstanceId() const
{
return (ntohs(((UINT16*)buffer)[INSTANCE_ID_OFFSET]));
}
void GetGrttResponse(struct timeval& grttResponse) const
{
grttResponse.tv_sec = ntohl(buffer[GRTT_RESPONSE_SEC_OFFSET]);
grttResponse.tv_usec = ntohl(buffer[GRTT_RESPONSE_USEC_OFFSET]);
}
NormAck::Type GetAckType() const {return (NormAck::Type)((UINT8*)buffer)[ACK_TYPE_OFFSET];}
UINT8 GetAckId() const {return ((UINT8*)buffer)[ACK_ID_OFFSET];}
UINT16 GetPayloadLength() const {return (length - header_length);}
const char* GetPayload() const {return (((char*)buffer) + header_length);}
protected:
enum
{
SERVER_ID_OFFSET = MSG_OFFSET/4,
INSTANCE_ID_OFFSET = ((SERVER_ID_OFFSET*4)+4)/2,
ACK_TYPE_OFFSET = (INSTANCE_ID_OFFSET*2)+2,
ACK_ID_OFFSET = ACK_TYPE_OFFSET + 1,
GRTT_RESPONSE_SEC_OFFSET = (ACK_ID_OFFSET + 1)/4,
GRTT_RESPONSE_USEC_OFFSET = ((GRTT_RESPONSE_SEC_OFFSET*4)+4)/4,
ACK_HEADER_LEN = (GRTT_RESPONSE_USEC_OFFSET*4)+4
};
}; // end class NormAckMsg
class NormAckFlushMsg : public NormAckMsg
{
public:
void Init()
{
SetType(ACK);
SetBaseHeaderLength(ACK_HEADER_LEN);
SetAckType(NormAck::FLUSH);
SetFecId(129); // only one supported for the moment
((UINT8*)buffer)[RESERVED_OFFSET] = 0;
length = ACK_HEADER_LEN+PAYLOAD_LENGTH;
}
// Note: must apply any header exts _before_ the payload is set
void SetFecId(UINT8 fecId)
{((UINT8*)buffer)[header_length+FEC_ID_OFFSET] = fecId;}
void SetObjectId(NormObjectId objectId)
{
((UINT16*)buffer)[(header_length/2)+OBJ_ID_OFFSET] = htons((UINT16)objectId);
}
void SetFecBlockId(const NormBlockId& blockId)
{
buffer[(header_length/4)+BLOCK_ID_OFFSET] = htonl((UINT32)blockId);
}
void SetFecBlockLen(UINT16 blockLen)
{
((UINT16*)buffer)[(header_length/2)+BLOCK_LEN_OFFSET] = htons(blockLen);
}
void SetFecSymbolId(UINT16 symbolId)
{
((UINT16*)buffer)[(header_length/2)+SYMBOL_ID_OFFSET] = htons(symbolId);
}
// Message processing
UINT8 GetFecId() {return ((UINT8*)buffer)[header_length+FEC_ID_OFFSET];}
NormObjectId GetObjectId() const
{
return (ntohs(((UINT16*)buffer)[(header_length/2)+OBJ_ID_OFFSET]));
}
NormBlockId GetFecBlockId() const
{
return (ntohl(buffer[(header_length/4)+BLOCK_ID_OFFSET]));
}
UINT16 GetFecBlockLen() const
{
return (ntohs(((UINT16*)buffer)[(header_length/2)+BLOCK_LEN_OFFSET]));
}
UINT16 GetFecSymbolId() const
{
return (ntohs(((UINT16*)buffer)[(header_length/2)+SYMBOL_ID_OFFSET]));
}
private:
// These are the payload offsets for "fec_id" = 129
// "fec_payload_id" field
enum
{
FEC_ID_OFFSET = 0,
RESERVED_OFFSET = FEC_ID_OFFSET + 1,
OBJ_ID_OFFSET = (RESERVED_OFFSET+1)/2,
BLOCK_ID_OFFSET = ((OBJ_ID_OFFSET*2)+2)/4,
BLOCK_LEN_OFFSET = ((BLOCK_ID_OFFSET*4)+4)/2,
SYMBOL_ID_OFFSET = ((BLOCK_LEN_OFFSET*2)+2)/2,
PAYLOAD_LENGTH = (SYMBOL_ID_OFFSET*2)+2
};
}; // end class NormAckFlushMsg
class NormReportMsg : public NormMsg
{
// (TBD)
}; // end class NormReportMsg
// One we've defined our basic message types, we
// do some unions so we can easily use these
// via casting or dereferencing the union members
class NormMessageQueue
{
public:
NormMessageQueue();
~NormMessageQueue();
void Destroy();
void Prepend(NormMsg* msg);
void Append(NormMsg* msg);
void Remove(NormMsg* msg);
NormMsg* RemoveHead();
NormMsg* RemoveTail();
NormMsg* GetHead() {return head;}
bool IsEmpty() {return ((NormMsg*)NULL == head);}
private:
NormMsg* head;
NormMsg* tail;
}; // end class NormMessageQueue
#endif // _NORM_MESSAGE