#ifndef _NORM_MESSAGE #define _NORM_MESSAGE // PROTOLIB includes #include "protokit.h" // standard includes #include // for memcpy(), etc #include #include // for rand(), etc #ifdef SIMULATE #define SIM_PAYLOAD_MAX (36+8) // MGEN message size + StreamPayloadHeaderLen() #endif // SIMULATE const UINT8 NORM_PROTOCOL_VERSION = 1; // (TBD) Alot of the "memcpy()" calls could be eliminated by // taking advantage of the alignment of NORM messsages const int NORM_ROBUST_FACTOR = 20; // default robust factor // 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); } // This class is used to describe object "size" and/or "offset" class NormObjectSize { public: NormObjectSize() : msb(0), lsb(0) {} NormObjectSize(UINT16 upper, UINT32 lower) : msb(upper), lsb(lower) {} NormObjectSize(UINT32 size) : msb(0), lsb(size) {} NormObjectSize(UINT16 size) : msb(0), lsb(size) {} NormObjectSize(const NormObjectSize& size) : msb(size.msb), lsb(size.lsb) {} UINT16 MSB() const {return msb;} UINT32 LSB() const {return lsb;} bool operator==(const NormObjectSize& size) const {return ((msb == size.msb) && (lsb == size.lsb));} bool operator>(const NormObjectSize& size) const { return ((msb == size.msb) ? (lsb > size.lsb) : (msb > size.msb)); } bool operator<(const NormObjectSize& size) const { return ((msb == size.msb) ? (lsb < size.lsb) : (msb < size.msb)); } bool operator<=(const NormObjectSize& size) const {return ((*this == size) || (*this=(const NormObjectSize& size) const {return ((*this == size) || (*this>size));} bool operator!=(const NormObjectSize& size) const {return ((lsb != size.lsb) || (msb != size.msb));} NormObjectSize operator+(const NormObjectSize& size) const; NormObjectSize operator-(const NormObjectSize& size) const; void operator+=(UINT32 increment) { UINT32 temp32 = lsb + increment; if (temp32 < lsb) msb++; lsb = temp32; } NormObjectSize operator+(UINT32 increment) const { NormObjectSize total; total.msb = msb; total.lsb = lsb + increment; if (total.lsb < lsb) total.msb++; return total; } NormObjectSize& operator++(int) { lsb++; if (0 == lsb) msb++; return (*this); } NormObjectSize operator*(const NormObjectSize& b) const; NormObjectSize operator/(const NormObjectSize& b) const; private: UINT16 msb; UINT32 lsb; }; // end class NormObjectSize typedef UINT32 NormNodeId; const NormNodeId NORM_NODE_INVALID = 0x00000000; const NormNodeId NORM_NODE_ANY = 0xffffffff; class NormObjectId { public: NormObjectId() {}; NormObjectId(UINT16 id) {value = id;} NormObjectId(const NormObjectId& id) {value = id.value;} operator UINT16() const {return value;} int operator-(const NormObjectId& id) const { int result = value - id.value; return ((0 == (result & 0x00008000)) ? (result & 0x0000ffff) : (((result != 0x00008000) || (value < id.value)) ? result | 0xffff0000 : result)); } bool operator<(const NormObjectId& id) const {return ((*this - id) < 0);} bool operator>(const NormObjectId& id) const {return ((*this - id) > 0);} bool operator<=(const NormObjectId& id) const {return ((value == id.value) || (*this=(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;} 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);} long operator-(const NormBlockId& id) const {return ((long)value - id.value);} bool operator<(const NormBlockId& id) const {return ((*this - id) < 0);} bool operator>(const NormBlockId& id) const {return ((*this - id) > 0);} NormBlockId& operator++(int ) {value++; return *this;} private: UINT32 value; }; // end class NormObjectId typedef UINT16 NormSymbolId; typedef NormSymbolId NormSegmentId; // Base class for NORM header extensions class NormHeaderExtension { public: enum Type { INVALID = 0, FTI = 1, // FEC Object Transmission Information (FTI) extension CC_FEEDBACK = 2, // NORM-CC Feedback extension CC_RATE = 128 // NORM-CC Rate extension }; NormHeaderExtension(); virtual void Init(char* theBuffer) { buffer = theBuffer; SetType(INVALID); SetWords(0); } void SetType(Type type) {buffer[TYPE_OFFSET] = (UINT8)type;} void SetWords(UINT8 words) {buffer[LENGTH_OFFSET] = words;} void AttachBuffer(const char* theBuffer) {buffer = (char*)theBuffer;} const char* GetBuffer() {return buffer;} Type GetType() const { return buffer ? (Type)((UINT8)buffer[TYPE_OFFSET]) : INVALID; } UINT16 GetLength() const { return buffer ? ((GetType() < 128) ? (buffer[LENGTH_OFFSET] << 2) : 4) : 0; } protected: enum { TYPE_OFFSET = 0, LENGTH_OFFSET = TYPE_OFFSET + 1 }; char* 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) { buffer[VERSION_OFFSET] = (buffer[VERSION_OFFSET] & 0x0f) | (version << 4); } void SetType(NormMsg::Type type) { buffer[TYPE_OFFSET] = (buffer[VERSION_OFFSET] & 0xf0) | (type & 0x0f); } void SetSequence(UINT16 sequence) { *((UINT16*)(buffer+SEQUENCE_OFFSET)) = htons(sequence); } void SetSourceId(NormNodeId sourceId) { *((UINT32*)(buffer+SOURCE_ID_OFFSET)) = htonl(sourceId); } void SetDestination(const ProtoAddress& dst) {addr = dst;} void AttachExtension(NormHeaderExtension& extension) { extension.Init(buffer+header_length); ExtendHeaderLength(extension.GetLength()); } // Message processing routines bool InitFromBuffer(UINT16 msgLength); UINT8 GetVersion() const {return buffer[VERSION_OFFSET];} NormMsg::Type GetType() const {return (Type)(buffer[TYPE_OFFSET] & 0x0f);} UINT16 GetHeaderLength() {return buffer[HDR_LEN_OFFSET] << 2;} UINT16 GetSequence() const { return (ntohs(*((UINT16*)(buffer+SEQUENCE_OFFSET)))); } NormNodeId GetSourceId() const { return (ntohl(*((UINT32*)(buffer+SOURCE_ID_OFFSET)))); } const ProtoAddress& GetDestination() const {return addr;} const ProtoAddress& GetSource() const {return addr;} const char* GetBuffer() {return 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 char* currentBuffer = ext.GetBuffer(); UINT16 nextOffset = currentBuffer ? (currentBuffer - buffer + ext.GetLength()) : header_length_base; bool result = (nextOffset < header_length); ext.AttachBuffer(result ? (buffer+nextOffset) : NULL); return result; } // For message reception and misc. char* AccessBuffer() {return buffer;} ProtoAddress& AccessAddress() {return addr;} protected: // Common message header offsets enum { VERSION_OFFSET = 0, TYPE_OFFSET = VERSION_OFFSET, HDR_LEN_OFFSET = VERSION_OFFSET + 1, SEQUENCE_OFFSET = HDR_LEN_OFFSET + 1, SOURCE_ID_OFFSET = SEQUENCE_OFFSET + 2, MSG_OFFSET = SOURCE_ID_OFFSET + 4 }; void SetBaseHeaderLength(UINT16 len) { buffer[HDR_LEN_OFFSET] = len >> 2; length = header_length_base = header_length = len; } void ExtendHeaderLength(UINT16 len) { header_length += len; length = header_length; buffer[HDR_LEN_OFFSET] = header_length >> 2; } char buffer[MAX_SIZE]; UINT16 length; 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 }; UINT16 GetSessionId() const { return (ntohs(*((UINT16*)(buffer+SESSION_ID_OFFSET)))); } UINT8 GetGrtt() const {return buffer[GRTT_OFFSET];} UINT8 GetBackoffFactor() const {return ((buffer[GSIZE_OFFSET] >> 4) & 0x0f);} UINT8 GetGroupSize() const {return (buffer[GSIZE_OFFSET] & 0x0f);} bool FlagIsSet(NormObjectMsg::Flag flag) const {return (0 != (flag & buffer[FLAGS_OFFSET]));} bool IsStream() const {return FlagIsSet(FLAG_STREAM);} UINT8 GetFecId() const {return buffer[FEC_ID_OFFSET];} NormObjectId GetObjectId() const { return (ntohs(*((UINT16*)(buffer+OBJ_ID_OFFSET)))); } // Message building routines void SetSessionId(UINT16 sessionId) { *((UINT16*)(buffer+SESSION_ID_OFFSET)) = htons(sessionId); } void SetGrtt(UINT8 grtt) {buffer[GRTT_OFFSET] = grtt;} void SetBackoffFactor(UINT8 backoff) { buffer[BACKOFF_OFFSET] = (buffer[GSIZE_OFFSET] & 0x0f) | (backoff << 4); } void SetGroupSize(UINT8 gsize) { buffer[GSIZE_OFFSET] = (buffer[GSIZE_OFFSET] & 0xf0) | gsize; } void ResetFlags() {buffer[FLAGS_OFFSET] = 0;} void SetFlag(NormObjectMsg::Flag flag) {buffer[FLAGS_OFFSET] |= flag;} void SetFecId(UINT8 fecId) {buffer[FEC_ID_OFFSET] = fecId;} void SetObjectId(const NormObjectId& objectId) { *((UINT16*)(buffer+OBJ_ID_OFFSET)) = htons((UINT16)objectId); } protected: enum { SESSION_ID_OFFSET = MSG_OFFSET, GRTT_OFFSET = SESSION_ID_OFFSET + 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, OBJ_MSG_OFFSET = OBJ_ID_OFFSET + 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(char* 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_OFFSET)) = htons(objectSize.MSB()); *((UINT32*)(buffer+OBJ_SIZE_OFFSET+2)) = 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_OFFSET))), ntohl(*((UINT32*)(buffer+OBJ_SIZE_OFFSET+2)))); } private: enum { OBJ_SIZE_OFFSET = LENGTH_OFFSET + 1, FEC_INSTANCE_OFFSET = OBJ_SIZE_OFFSET + 6, SEG_SIZE_OFFSET = FEC_INSTANCE_OFFSET +2, FEC_NDATA_OFFSET = SEG_SIZE_OFFSET + 2, FEC_NPARITY_OFFSET = FEC_NDATA_OFFSET + 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 (buffer + header_length);} // Note: apply any header extensions first void SetInfo(const char* data, UINT16 size) { memcpy(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) { UINT32 temp32 = htonl((UINT32)blockId); memcpy(buffer+BLOCK_ID_OFFSET, &temp32, 4); } void SetFecBlockLen(UINT16 blockLen) { blockLen = htons(blockLen); memcpy(buffer+BLOCK_LEN_OFFSET, &blockLen, 2); } void SetFecSymbolId(UINT16 symbolId) { symbolId = htons(symbolId); memcpy(buffer+SYMBOL_ID_OFFSET, &symbolId, 2); } // 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 (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(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 (buffer+payloadIndex); } // Message processing methods NormBlockId GetFecBlockId() const { return (ntohl(*((UINT32*)(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 (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 (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);} static void WriteStreamPayloadLength(char* payload, UINT16 len) { *((UINT16*)(payload+PAYLOAD_LENGTH_OFFSET)) = htons(len); } static void WriteStreamPayloadOffset(char* payload, UINT32 offset) { *((UINT32*)(payload+PAYLOAD_OFFSET_OFFSET)) = htonl(offset); } static UINT16 ReadStreamPayloadLength(const char* payload) { return (ntohs(*((UINT16*)(payload+PAYLOAD_LENGTH_OFFSET)))); } static UINT32 ReadStreamPayloadOffset(const char* payload) { return ntohl(*((UINT32*)(payload+PAYLOAD_OFFSET_OFFSET))); } private: enum { BLOCK_ID_OFFSET = OBJ_MSG_OFFSET, BLOCK_LEN_OFFSET = BLOCK_ID_OFFSET + 4, SYMBOL_ID_OFFSET = BLOCK_LEN_OFFSET + 2, DATA_HEADER_LEN = SYMBOL_ID_OFFSET + 2 }; enum { PAYLOAD_RESERVED_OFFSET = 0, PAYLOAD_LENGTH_OFFSET = PAYLOAD_RESERVED_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 SetSessionId(UINT16 sessionId) { *((UINT16*)(buffer+SESSION_ID_OFFSET)) = htons(sessionId); } void SetGrtt(UINT8 quantizedGrtt) {buffer[GRTT_OFFSET] = quantizedGrtt;} void SetBackoffFactor(UINT8 backoff) { buffer[BACKOFF_OFFSET] = (buffer[GSIZE_OFFSET] & 0x0f) | (backoff << 4); } void SetGroupSize(UINT8 gsize) { buffer[GSIZE_OFFSET] = (buffer[GSIZE_OFFSET] & 0xf0) | gsize; } void SetFlavor(NormCmdMsg::Flavor flavor) {buffer[FLAVOR_OFFSET] = flavor;} // Message processing UINT16 GetSessionId() const {return (ntohs(*((UINT16*)(buffer+SESSION_ID_OFFSET))));} UINT8 GetGrtt() const {return buffer[GRTT_OFFSET];} UINT8 GetBackoffFactor() const {return ((buffer[GSIZE_OFFSET] >> 4) & 0x0f);} UINT8 GetGroupSize() const {return (buffer[GSIZE_OFFSET] & 0x0f);} NormCmdMsg::Flavor GetFlavor() const {return (Flavor)buffer[FLAVOR_OFFSET];} protected: friend class NormMsg; enum { SESSION_ID_OFFSET = MSG_OFFSET, GRTT_OFFSET = SESSION_ID_OFFSET + 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) {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) { *((UINT32*)(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; *((UINT32*)(buffer+length)) = htonl(nodeId); length += 4; return true; } // Message processing UINT8 GetFecId() {return buffer[FEC_ID_OFFSET];} NormObjectId GetObjectId() const { return (ntohs(*((UINT16*)(buffer+OBJ_ID_OFFSET)))); } NormBlockId GetFecBlockId() const { return (ntohl(*((UINT32*)(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 (UINT32*)(buffer+header_length);} NormNodeId GetAckingNodeId(UINT16 index) const { return (ntohl(*((UINT32*)(buffer+header_length+(index<<2))))); } private: enum { FEC_ID_OFFSET = FLAVOR_OFFSET + 1, OBJ_ID_OFFSET = FEC_ID_OFFSET + 1, BLOCK_ID_OFFSET = OBJ_ID_OFFSET + 2, BLOCK_LEN_OFFSET = BLOCK_ID_OFFSET + 4, SYMBOL_ID_OFFSET = BLOCK_LEN_OFFSET + 2, FLUSH_HEADER_LEN = SYMBOL_ID_OFFSET + 2 }; }; // end class NormCmdFlushMsg class NormCmdEotMsg : public NormCmdMsg { public: void Init() { SetType(CMD); SetFlavor(EOT); SetBaseHeaderLength(EOT_HEADER_LEN); memset(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) {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) { *((UINT32*)(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)) = htons((UINT16)objectId); length += 2; return true; } // Message processing UINT8 GetFecId() {return buffer[FEC_ID_OFFSET];} NormObjectId GetObjectId() const { return (ntohs(*((UINT16*)(buffer+OBJ_ID_OFFSET)))); } NormBlockId GetFecBlockId() const { return (ntohl(*((UINT32*)(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+(index << 1))))); } private: enum { FEC_ID_OFFSET = FLAVOR_OFFSET + 1, OBJ_ID_OFFSET = FEC_ID_OFFSET + 1, BLOCK_ID_OFFSET = OBJ_ID_OFFSET + 2, BLOCK_LEN_OFFSET = BLOCK_ID_OFFSET + 4, SYMBOL_ID_OFFSET = BLOCK_LEN_OFFSET + 2, SQUELCH_HEADER_LEN = SYMBOL_ID_OFFSET + 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); buffer[RESERVED_OFFSET] = 0; } void SetCCSequence(UINT16 ccSequence) { *((UINT16*)(buffer+CC_SEQUENCE_OFFSET)) = htons(ccSequence); } void SetSendTime(const struct timeval& sendTime) { *((UINT32*)(buffer+SEND_TIME_OFFSET)) = htonl(sendTime.tv_sec); *((UINT32*)(buffer+SEND_TIME_OFFSET+4)) = htonl(sendTime.tv_usec); } UINT16 GetCCSequence() const { return (ntohs(*((UINT16*)(buffer+CC_SEQUENCE_OFFSET)))); } void GetSendTime(struct timeval& sendTime) const { sendTime.tv_sec = ntohl(*((UINT32*)(buffer+SEND_TIME_OFFSET))); sendTime.tv_usec = ntohl(*((UINT32*)(buffer+SEND_TIME_OFFSET+4))); } bool AppendCCNode(UINT16 segMax, NormNodeId nodeId, UINT8 flags, UINT8 rtt, UINT16 rate) { if ((length-header_length+CC_ITEM_SIZE)> segMax) return false; char* ptr = buffer+length; *((UINT32*)(ptr+CC_NODE_ID_OFFSET)) = htonl(nodeId); ptr[CC_FLAGS_OFFSET] = flags; 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, SEND_TIME_OFFSET = CC_SEQUENCE_OFFSET + 2, CC_HEADER_LEN = SEND_TIME_OFFSET + 8 }; 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, CC_ITEM_SIZE = CC_RATE_OFFSET + 2 }; }; // end NormCmdCCMsg class NormCCRateExtension : public NormHeaderExtension { public: virtual void Init(char* theBuffer) { AttachBuffer(theBuffer); SetType(CC_RATE); 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 }; }; // 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(char* 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 UnsetFlag(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 char* 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, ITEM_LIST_OFFSET = LENGTH_OFFSET + 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, BLOCK_ID_OFFSET = OBJ_ID_OFFSET + 2, BLOCK_LEN_OFFSET = BLOCK_ID_OFFSET + 4, SYMBOL_ID_OFFSET = BLOCK_LEN_OFFSET + 2 }; Form form; int flags; UINT16 length; char* buffer; UINT16 buffer_len; }; // 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() {buffer[FLAGS_OFFSET] = 0;} void SetFlag(NormCmdRepairAdvMsg::Flag flag) {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, 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 | buffer[FLAGS_OFFSET])); } //char* AccessRepairContent() {return (buffer + header_length);} const char* GetRepairContent() const {return (buffer + header_length);} 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(char* theBuffer) { AttachBuffer(theBuffer); SetType(CC_FEEDBACK); SetWords(3); buffer[CC_FLAGS_OFFSET] = 0; *((UINT16*)(buffer+CC_RESERVED_OFFSET)) = 0; } void SetCCSequence(UINT16 ccSequence) { *((UINT16*)(buffer+CC_SEQUENCE_OFFSET)) = htons(ccSequence); } void ResetCCFlags() {buffer[CC_FLAGS_OFFSET] = 0;} void SetCCFlag(NormCC::Flag flag) {buffer[CC_FLAGS_OFFSET] |= (UINT8)flag;} void SetCCRtt(UINT8 ccRtt) {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 buffer[CC_FLAGS_OFFSET];} bool CCFlagIsSet(NormCC::Flag flag) const { return (0 != ((UINT8)flag & buffer[CC_FLAGS_OFFSET])); } UINT8 GetCCRtt() {return 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, CC_FLAGS_OFFSET = CC_SEQUENCE_OFFSET + 2, CC_RTT_OFFSET = CC_FLAGS_OFFSET + 1, CC_LOSS_OFFSET = CC_RTT_OFFSET + 1, CC_RATE_OFFSET = CC_LOSS_OFFSET + 2, CC_RESERVED_OFFSET = CC_RATE_OFFSET + 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); buffer[RESERVED_OFFSET] = 0; } // Message building void SetAckType(NormAck::Type ackType) {buffer[ACK_TYPE_OFFSET] = (UINT8)ackType;} void SetAckId(UINT8 ackId) {buffer[ACK_ID_OFFSET] = ackId;} void ResetAckingNodeList() {length = header_length;} bool AppendAckingNode(NormNodeId nodeId, UINT16 segmentSize) { if ((length - header_length + 4) > segmentSize) return false; *((UINT32*)(buffer+length)) = htonl(nodeId); length += 4; return true; } // Message processing NormAck::Type GetAckType() const {return (NormAck::Type)buffer[ACK_TYPE_OFFSET];} UINT8 GetAckId() const {return buffer[ACK_ID_OFFSET];} UINT16 GetAckingNodeCount() const {return ((length - header_length) >> 2);} NormNodeId GetAckingNodeId(UINT16 index) const { return (ntohl(*((UINT32*)(buffer+header_length+(index<<2))))); } 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(buffer+RESERVED_OFFSET, 0, 3); } bool SetContent(const char* content, UINT16 contentLen, UINT16 segmentSize) { UINT16 len = MIN(contentLen, segmentSize); memcpy(buffer+header_length, content, len); return (contentLen <= segmentSize); } UINT16 GetContentLength() {return (length - header_length);} const char* GetContent() {return (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) { *((UINT32*)(buffer+SERVER_ID_OFFSET)) = htonl(serverId); } void SetSessionId(UINT16 sessionId) { *((UINT16*)(buffer+SESSION_ID_OFFSET)) = htons(sessionId); } void SetGrttResponse(const struct timeval& grttResponse) { *((UINT32*)(buffer+GRTT_RESPONSE_OFFSET)) = htonl(grttResponse.tv_sec); *((UINT32*)(buffer+GRTT_RESPONSE_OFFSET+4)) = 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, buflen); } UINT16 PackRepairRequest(NormRepairRequest& request) { UINT16 requestLength = request.Pack(); length += requestLength; return requestLength; } // Message processing NormNodeId GetServerId() const { return (ntohl(*((UINT32*)(buffer+SERVER_ID_OFFSET)))); } UINT16 GetSessionId() const { return (ntohs(*((UINT16*)(buffer+SESSION_ID_OFFSET)))); } void GetGrttResponse(struct timeval& grttResponse) const { grttResponse.tv_sec = ntohl(*((UINT32*)(buffer+GRTT_RESPONSE_OFFSET))); grttResponse.tv_usec = ntohl(*((UINT32*)(buffer+GRTT_RESPONSE_OFFSET+4))); } //char* AccessRepairContent() {return (buffer + header_length);} const char* GetRepairContent() const {return (buffer + header_length);} 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, buflen); } private: enum { SERVER_ID_OFFSET = MSG_OFFSET, SESSION_ID_OFFSET = SERVER_ID_OFFSET + 4, RESERVED_OFFSET = SESSION_ID_OFFSET + 2, GRTT_RESPONSE_OFFSET = SERVER_ID_OFFSET + 4, NACK_HEADER_LEN = GRTT_RESPONSE_OFFSET + 8 }; }; // 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) { *((UINT32*)(buffer+SERVER_ID_OFFSET)) = htonl(serverId); } void SetSessionId(UINT16 sessionId) { *((UINT16*)(buffer+SESSION_ID_OFFSET)) = htons(sessionId); } void SetAckType(NormAck::Type ackType) {buffer[ACK_TYPE_OFFSET] = (UINT8)ackType;} void SetAckId(UINT8 ackId) {buffer[ACK_ID_OFFSET] = ackId;} void SetGrttResponse(const struct timeval& grttResponse) { *((UINT32*)(buffer+GRTT_RESPONSE_OFFSET)) = htonl(grttResponse.tv_sec); *((UINT32*)(buffer+GRTT_RESPONSE_OFFSET+4)) = htonl(grttResponse.tv_usec); } bool SetAckPayload(const char* payload, UINT16 payloadLen, UINT16 segmentSize) { UINT16 len = MIN(payloadLen, segmentSize); memcpy(buffer+header_length, payload, len); return (payloadLen <= segmentSize); } // Message processing NormNodeId GetServerId() const { return (ntohl(*((UINT32*)(buffer+SERVER_ID_OFFSET)))); } UINT16 GetSessionId() const { return (ntohs(*((UINT16*)(buffer+SESSION_ID_OFFSET)))); } void GetGrttResponse(struct timeval& grttResponse) const { grttResponse.tv_sec = ntohl(*((UINT32*)(buffer+GRTT_RESPONSE_OFFSET))); grttResponse.tv_usec = ntohl(*((UINT32*)(buffer+GRTT_RESPONSE_OFFSET+4))); } NormAck::Type GetAckType() const {return (NormAck::Type)buffer[ACK_TYPE_OFFSET];} UINT8 GetAckId() const {return buffer[ACK_ID_OFFSET];} UINT16 GetPayloadLength() const {return (length - header_length);} const char* GetPayload() const {return (buffer + header_length);} private: enum { SERVER_ID_OFFSET = MSG_OFFSET, SESSION_ID_OFFSET = SERVER_ID_OFFSET + 4, ACK_TYPE_OFFSET = SESSION_ID_OFFSET + 2, ACK_ID_OFFSET = ACK_TYPE_OFFSET + 1, GRTT_RESPONSE_OFFSET = ACK_ID_OFFSET + 1, ACK_HEADER_LEN = GRTT_RESPONSE_OFFSET + 8 }; }; // end class NormAckMsg 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(); bool IsEmpty() {return ((NormMsg*)NULL == head);} private: NormMsg* head; NormMsg* tail; }; // end class NormMessageQueue #endif // _NORM_MESSAGE