pull/2/head
Jeff Weston 2019-09-11 11:21:03 -04:00
commit 565afa5529
31 changed files with 13122 additions and 0 deletions

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# protolib
protolib/
# build executables
build/
lib/
makefiles/norm
makefiles/normMsgr
makefiles/normStreamer
makefiles/npc
makefiles/raft
# OS generated files #
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

28
LICENSE Normal file
View File

@ -0,0 +1,28 @@
/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
*
* (1) source code distributions retain this paragraph in its entirety,
*
* (2) distributions including binary code include this paragraph in
* its entirety in the documentation or other materials provided
* with the distribution.
*
* "This product includes software written and developed
* by Code 5520 of the Naval Research Laboratory (NRL)."
*
* The name of NRL, the name(s) of NRL employee(s), or any entity
* of the United States Government may not be used to endorse or
* promote products derived from this software, nor does the
* inclusion of the NRL written and developed software directly or
* indirectly suggest NRL or United States Government endorsement
* of this product.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
********************************************************************/

46
README.md Normal file
View File

@ -0,0 +1,46 @@
NORM PRELIMINARY CODE RELEASE
These directories contain source code for a very preliminary
implementation of the Nack Oriented Reliable Multicast (NORM)
protocol. Currently, a very dumb "norm" command-line application
(which currently takes no command-line arguments) is
created. This application creates a NormSession and acts
both as a sender and receiver to the multicast group. It
creates a "NORM_STREAM" object and writes repeated,
continuous strings of "aaaaaaa ..." to the stream. As the
stream is received, the client (receiver) portion of the
"norm" app writes a notices of a successful Read()
operation from the stream.
This currently uses a fixed transmission rate of 64 kbps
and a Reed-Solomon FEC encoding block with 20 user data
segments and calculates 8 parity segments per coding
block. Four parity segments are sent at the end of each
coding block as "auto parity". The current NORM code is
currently automatically discarding segment 9 of the
received data stream to test the FEC encoding/decoding and
stream buffer routines.
The code does not currently generate any NACK messages,
but the routines to perform checks for losses is in place
and the routines for building NACK messages are in place,
so the addition of the timer installation routines to
schedule NACK back-off and subsequent transmission will be
added very soon. Then, routines will be added for the
server (sender) side to process received NACKs and
subsequently provide repair messages. Then, client-side
routines for NACK suppression will be added. After that,
the final details of GRTT collection, unicast feedback
suppression will be added and finally one or more
congestion control schemes will be included.
The purpose of this release is to illustrate routines to
build and parse the messages currently defined in the NORM
Internet Draft.
The current code only has Makefiles for Unix platforms, but the
code could be assembled as Win32 project under VC++.
The NORM code depends upon the current "Protolib" release.
See <http://pf.itd.nrl.navy.mil> for that code.

4449
common/galois.cpp Normal file

File diff suppressed because it is too large Load Diff

44
common/galois.h Normal file
View File

@ -0,0 +1,44 @@
/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
*
* (1) source code distributions retain this paragraph in its entirety,
*
* (2) distributions including binary code include this paragraph in
* its entirety in the documentation or other materials provided
* with the distribution, and
*
* (3) all advertising materials mentioning features or use of this
* software display the following acknowledgment:
*
* "This product includes software written and developed
* by Brian Adamson and Joe Macker of the Naval Research
* Laboratory (NRL)."
*
* The name of NRL, the name(s) of NRL employee(s), or any entity
* of the United States Government may not be used to endorse or
* promote products derived from this software, nor does the
* inclusion of the NRL written and developed software directly or
* indirectly suggest NRL or United States Government endorsement
* of this product.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
********************************************************************/
#ifndef _GALOIS
#define _GALOIS
extern const unsigned char GINV[256];
extern const unsigned char GEXP[512];
extern const unsigned char GMULT[256][256];
inline unsigned char gexp(unsigned int x) {return GEXP[x];}
inline unsigned char gmult(unsigned int x, unsigned int y) {return GMULT[x][y];}
inline unsigned char ginv(unsigned int x) {return GINV[x];}
#endif // _GALOIS

250
common/normApp.cpp Normal file
View File

@ -0,0 +1,250 @@
// norm.cpp - Command-line NORM application
#include "protoLib.h"
#include "normSession.h"
#include <stdio.h> // for stdout/stderr printouts
#include <signal.h> // for SIGTERM/SIGINT handling
class NormApp : public NormController
{
public:
NormApp();
virtual ~NormApp();
bool OnStartup();
int MainLoop() {return dispatcher.Run();}
void Stop(int exitCode) {dispatcher.Stop(exitCode);}
void OnShutdown();
private:
virtual void Notify(NormController::Event event,
class NormSessionMgr* sessionMgr,
class NormSession* session,
class NormServerNode* server,
class NormObject* object);
static void SignalHandler(int sigNum);
EventDispatcher dispatcher;
NormSessionMgr session_mgr;
NormSession* session;
NormStreamObject* stream;
}; // end class NormApp
void NormApp::Notify(NormController::Event event,
class NormSessionMgr* sessionMgr,
class NormSession* session,
class NormServerNode* server,
class NormObject* object)
{
switch (event)
{
case TX_QUEUE_EMPTY:
//TRACE("NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
if (object == stream)
{
char text[256];
memset(text, 'a', 256);
stream->Write(text, 256);
}
break;
case RX_OBJECT_NEW:
//TRACE("NormApp::Notify(RX_OBJECT_NEW) ...\n");
{
switch (object->GetType())
{
case NormObject::STREAM:
{
const NormObjectSize& size = object->Size();
if (!((NormStreamObject*)object)->Accept(size.LSB()))
{
TRACE("stream object accept error!\n");
}
}
break;
case NormObject::FILE:
case NormObject::DATA:
TRACE("NormApp::Notify() FILE/DATA objects not supported...\n");
break;
}
}
break;
case RX_OBJECT_UPDATE:
//TRACE("NormApp::Notify(RX_OBJECT_UPDATE) ...\n");
switch (object->GetType())
{
case NormObject::STREAM:
{
// Read the stream
char buffer[2048];
unsigned int nBytes;
while ((nBytes = ((NormStreamObject*)object)->Read(buffer, 2048)))
{
for (unsigned int i =0; i < nBytes; i++)
{
if ('a' != buffer[i])
{
TRACE("NormApp::Notify() bad data received!\n");
break;
}
}
buffer[32] = '\0';
DMSG(0, "NormApp::Notify() stream read %u bytes: \"%s\"\n", nBytes, buffer);
}
}
break;
case NormObject::FILE:
case NormObject::DATA:
TRACE("NormApp::Notify() FILE/DATA objects not supported...\n");
break;
}
break;
}
}
NormApp::NormApp()
: session(NULL), stream(NULL)
{
// Init tx_timer for 1.0 second interval, infinite repeats
session_mgr.Init(EventDispatcher::TimerInstaller, &dispatcher,
EventDispatcher::SocketInstaller, &dispatcher,
this);
}
NormApp::~NormApp()
{
}
bool NormApp::OnStartup()
{
#ifdef WIN32
if (!dispatcher.Win32Init())
{
fprintf(stderr, "norm:: Win32Init() error!\n");
return false;
}
#endif // WIN32
signal(SIGTERM, SignalHandler);
signal(SIGINT, SignalHandler);
session = session_mgr.NewSession("224.225.1.5", 5005);
if (session)
{
if (!session->StartServer(1024*1024, 256, 20, 8))
{
DMSG(0, "NormApp::OnStartup() start server error!\n");
session_mgr.Destroy();
return false;
}
session->ServerSetAutoParity(4);
//session->SetLoopback(true);
// Open a stream object to write to
stream = session->QueueTxStream(10*1024);
if (!stream)
{
DMSG(0, "NormApp::OnStartup() queue tx stream error!\n");
session_mgr.Destroy();
return false;
}
char text[256];
memset(text, 'a', 256);
stream->Write(text, 256);
return true;
}
else
{
return false;
}
} // end NormApp::OnStartup()
void NormApp::OnShutdown()
{
session_mgr.Destroy();
} // end NormApp::OnShutdown()
// Out application instance (global for SignalHandler)
NormApp theApp;
// Use "main()" for UNIX and WIN32 console apps,
// "WinMain()" for non-console WIN32
// (VC++ uses the "_CONSOLE_ macro to indicate build type)
#if defined(WIN32) && !defined(_CONSOLE)
int PASCAL WinMain(HINSTANCE instance, HINSTANCE prevInst, LPSTR cmdline, int cmdshow)
#else
int main(int argc, char* argv[])
#endif
{
#ifdef WIN32
// Hack to determine if Win32 console application was launched
// independently or from a pre-existing console window
bool pauseForUser = false;
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
if (INVALID_HANDLE_VALUE != hConsoleOutput)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
pauseForUser = ((csbi.dwCursorPosition.X==0) && (csbi.dwCursorPosition.Y==0));
if ((csbi.dwSize.X<=0) || (csbi.dwSize.Y <= 0)) pauseForUser = false;
}
else
{
// We're not a "console" application, so create one
// This could be commented out or made a command-line option
OpenDebugWindow();
pauseForUser = true;
}
#endif // WIN32
int exitCode = 0;
if (theApp.OnStartup())
{
exitCode = theApp.MainLoop();
theApp.OnShutdown();
fprintf(stderr, "norm: Done.\n");
#ifdef WIN32
// If Win32 console is going to disappear, pause for user before exiting
if (pauseForUser)
{
printf ("Program Finished - Hit <Enter> to exit");
getchar();
}
#endif // WIN32
}
else
{
fprintf(stderr, "norm: Error initializing application!\n");
return -1;
}
return exitCode; // exitCode contains "signum" causing exit
} // end main();
void NormApp::SignalHandler(int sigNum)
{
switch(sigNum)
{
case SIGTERM:
case SIGINT:
theApp.Stop(sigNum); // causes theApp's main loop to exit
break;
default:
fprintf(stderr, "norm: Unexpected signal: %d\n", sigNum);
break;
}
} // end NormApp::SignalHandler()

1234
common/normBitmask.cpp Normal file

File diff suppressed because it is too large Load Diff

184
common/normBitmask.h Normal file
View File

@ -0,0 +1,184 @@
#ifndef _NORM_BITMASK_
#define _NORM_BITMASK_
#include "debug.h" // for PROTO_DEBUG stuff
#include <string.h> // for memset()
#include <stdio.h> // for fprintf()
/***************************************************************
* This class also provides space-efficient binary storage.
* It's pretty much just a flat-indexed array of bits, but
* keeps some state to be relatively efficient for various
* operations.
*/
class NormBitmask
{
// Methods
public:
NormBitmask();
~NormBitmask();
bool Init(unsigned long numBits);
void Destroy();
unsigned long Size() {return num_bits;}
void Clear() // set to all zero's
{
memset(mask, 0, mask_len);
first_set = num_bits;
};
void Reset() // set to all one's
{
memset(mask, 0xff, (mask_len-1));
mask[mask_len-1] = 0x00ff << ((8 - (num_bits & 0x07)) & 0x07);
first_set = 0;
}
bool IsSet() const {return (first_set < num_bits);}
unsigned long FirstSet() const {return first_set;}
unsigned long LastSet() const
{
return ((first_set < num_bits) ?
PrevSet(num_bits - 1) : num_bits);
}
bool Test(unsigned long index) const
{
return ((index < num_bits) ?
(0 != (mask[(index >> 3)] & (0x80 >> (index & 0x07)))) :
false);
}
bool CanSet(unsigned long index) const
{return (index < num_bits);}
bool Set(unsigned long index)
{
if (index < num_bits)
{
mask[(index >> 3)] |= (0x80 >> (index & 0x07));
(index < first_set) ? (first_set = index) : 0;
return true;
}
else
{
return false;
}
}
bool Unset(unsigned long index)
{
if (index < num_bits)
{
mask[(index >> 3)] &= ~(0x80 >> (index & 0x07));
(index == first_set) ? first_set = NextSet(index) : 0;
}
return true;
}
bool Invert(unsigned long index)
{return (Test(index) ? Unset(index) : Set(index));}
bool SetBits(unsigned long baseIndex, unsigned long count);
bool UnsetBits(unsigned long baseIndex, unsigned long count);
unsigned long NextSet(unsigned long index) const;
unsigned long PrevSet(unsigned long index) const;
unsigned long NextUnset(unsigned long index) const;
bool Copy(const NormBitmask &b); // this = b
bool Add(const NormBitmask & b); // this = this | b
bool Subtract(const NormBitmask & b); // this = this & ~b
bool XCopy(const NormBitmask & b); // this = ~this & b
bool Multiply(const NormBitmask & b); // this = this & b
bool Xor(const NormBitmask & b); // this = this ^ b
void Display(FILE* stream);
// Members
private:
unsigned char* mask;
unsigned long mask_len;
unsigned long num_bits;
unsigned long first_set; // index of lowest _set_ bit
}; // end class NormBitmask
/***************************************************************
* This class also provides space-efficient binary storage.
* More than just a flat-indexed array of bits, this
* class can also automatically act as a sliding
* window buffer as long as the range of set bit
* indexes fall within the number of storage bits
* for which the class initialized.
*/
class NormSlidingMask
{
public:
NormSlidingMask();
~NormSlidingMask();
bool Init(long numBits);
void Destroy();
long Size() const {return num_bits;}
void Clear()
{
memset(mask, 0, mask_len);
start = end = num_bits;
offset = 0;
}
void Reset(unsigned long index = 0)
{
memset(mask, 0xff, mask_len);
mask[mask_len-1] = 0x00ff << ((8 - (num_bits & 0x07)) & 0x07);
start = 0;
end = num_bits - 1;
offset = index;
}
bool IsSet() const {return (start < num_bits);}
unsigned long FirstSet() const {return offset;}
unsigned long LastSet() const
{
long n = end - start;
n = (n < 0) ? (n + num_bits) : n;
return (n + offset);
}
bool Test(unsigned long index) const;
bool CanSet(unsigned long index) const;
bool Set(unsigned long index);
bool Unset(unsigned long index);
bool Invert(unsigned long index)
{return (Test(index) ? Unset(index): Set(index));}
bool SetBits(unsigned long index, long count);
bool UnsetBits(unsigned long index, long count);
// These return "FirstSet()+Size()" when finding nothing
unsigned long NextSet(unsigned long index) const;
unsigned long PrevSet(unsigned long index) const;
static unsigned long RawNextSet(const char* mask, long index, long start);
static unsigned long RawPrevSet(const char* mask, long index, long end);
bool Copy(const NormSlidingMask& b); // this = b
bool Add(const NormSlidingMask & b); // this = this | b
bool Subtract(const NormSlidingMask & b); // this = this & ~b
bool XCopy(const NormSlidingMask & b); // this = ~this & b
bool Multiply(const NormSlidingMask & b); // this = this & b
bool Xor(const NormSlidingMask & b); // this = this ^ b
void Display(FILE* stream);
private:
unsigned char* mask;
unsigned long mask_len;
long num_bits;
long start;
long end;
unsigned long offset;
}; // end class NormSlidingMask
#endif // _NORM_BITMASK_

420
common/normEncoder.cpp Normal file
View File

@ -0,0 +1,420 @@
/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
*
* (1) source code distributions retain this paragraph in its entirety,
*
* (2) distributions including binary code include this paragraph in
* its entirety in the documentation or other materials provided
* with the distribution, and
*
* (3) all advertising materials mentioning features or use of this
* software display the following acknowledgment:
*
* "This product includes software written and developed
* by Brian Adamson and Joe Macker of the Naval Research
* Laboratory (NRL)."
*
* The name of NRL, the name(s) of NRL employee(s), or any entity
* of the United States Government may not be used to endorse or
* promote products derived from this software, nor does the
* inclusion of the NRL written and developed software directly or
* indirectly suggest NRL or United States Government endorsement
* of this product.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
********************************************************************/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "normEncoder.h"
#include "galois.h" // for Galois math routines
#include "debug.h"
NormEncoder::NormEncoder()
: npar(0), vecSize(0),
genPoly(NULL), scratch(NULL)
{
} // end NormEncoder::NormEncoder()
NormEncoder::~NormEncoder()
{
if (genPoly) Destroy();
}
bool NormEncoder::Init(int numParity, int vecSizeMax)
{
// Debugging assertions
ASSERT((numParity>=0)&&(numParity<129));
ASSERT(vecSizeMax >= 0);
if (genPoly) Destroy();
npar = numParity;
vecSize = vecSizeMax;
// Create generator polynomial
if(!CreateGeneratorPolynomial())
{
DMSG(0, "NormEncoder: Error creating generator polynomial!\n");
return false;
}
// Allocate scratch space for encoding
if(!(scratch = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormEncoder: Error allocating memory for encoder scratch space: %s\n",
strerror(errno));
Destroy();
return false;
}
return true;
} // end NormEncoder::Init()
// Free memory allocated for encoder state (Encoder must be re-inited before use)
void NormEncoder::Destroy()
{
if(scratch)
{
delete []scratch;
scratch = NULL;
}
if (genPoly)
{
delete genPoly;
genPoly = NULL;
}
} // end NormEncoder::Destroy()
bool NormEncoder::CreateGeneratorPolynomial()
{
unsigned char *tp, *tp1, *tp2;
int degree = 2*npar;
if(genPoly) delete genPoly;
if(!(genPoly = new unsigned char[npar+1]))
{
DMSG(0, "NormEncoder: Error allocating memory for generator polynomial: %s\n",
strerror(errno));
return false;
}
/* Allocate memory for temporary polynomial arrays */
if(!(tp = new unsigned char[2*degree]))
{
DMSG(0, "NormEncoder: Error allocating memory while computing genpoly: %s\n",
strerror(errno));
delete genPoly;
return false;
}
if(!(tp1 = new unsigned char[2*degree]))
{
delete tp;
delete genPoly;
DMSG(0, "NormEncoder: Error allocating memory while computing genpoly: %s\n",
strerror(errno));
return false;
}
if(!(tp2 = new unsigned char[2*degree]))
{
delete tp1;
delete tp;
delete genPoly;
DMSG(0, "NormEncoder: Error allocating memory while computing genpoly: %s\n",
strerror(errno));
return false;
}
// multiply (x + a^n) for n = 1 to npar
memset(tp1, 0, degree*sizeof(unsigned char));
tp1[0] = 1;
for (int n = 1; n <= npar; n++)
{
memset(tp, 0, degree*sizeof(unsigned char));
tp[0] = gexp(n); // set up x+a^n
tp[1] = 1;
// Polynomial multiplication
memset(genPoly, 0, (npar+1)*sizeof(unsigned char));
for (int i = 0; i < degree; i++)
{
memset(&tp2[degree], 0, degree*sizeof(unsigned char));
// Scale tp2 by p1[i]
for(int j=0; j<degree; j++) tp2[j]=gmult(tp1[j], tp[i]);
// Mult(shift) tp2 right by i
for (int j = (degree*2)-1; j >= i; j--) tp2[j] = tp2[j-i];
memset(tp2, 0, i*sizeof(unsigned char));
// Add into partial product
for(int j=0; j < (npar+1); j++) genPoly[j] ^= tp2[j];
}
memcpy(tp1, genPoly, (npar+1)*sizeof(unsigned char));
memset(&tp1[npar+1], 0, (2*degree)-(npar+1));
}
delete tp2;
delete tp1;
delete tp;
return true;
} // end NormEncoder::CreateGeneratorPolynomial()
// Encode data vectors one at a time. The user of this function
// must keep track of when parity is ready for transmission
// Parity data is written to list of parity vectors supplied by caller
void NormEncoder::Encode(const char *data, char **pVec)
{
#if defined(NS2) || defined(OPNET)
return; // lobotomize FEC for faster simulations
#endif
int i, j;
unsigned char *userData, *LSFR1, *LSFR2, *pVec0;
int npar_minus_one = npar - 1;
unsigned char *gen_poly = &genPoly[npar_minus_one];
ASSERT(scratch); // Make sure it's been init'd first
// Assumes parity vectors are zero-filled at block start !!!
// Copy pVec[0] for use in calculations
memcpy(scratch, pVec[0], vecSize);
if (npar > 1)
{
for(i = 0; i < npar_minus_one; i++)
{
pVec0 = scratch;
userData = (unsigned char *) data;
LSFR1 = (unsigned char *) pVec[i];
LSFR2 = (unsigned char *) pVec[i+1];
for(j = 0; j < vecSize; j++)
*LSFR1++ = *LSFR2++ ^
gmult(*gen_poly, (*userData++ ^ *pVec0++));
gen_poly--;
}
}
pVec0 = scratch;
userData = (unsigned char *) data;
LSFR1 = (unsigned char *) pVec[npar_minus_one];
for(j = 0; j < vecSize; j++)
*LSFR1++ = gmult(*gen_poly, (*userData++ ^ *pVec0++));
} // end NormEncoder::Encode()
/********************************************************************************
* NormDecoder implementation routines
*/
NormDecoder::NormDecoder()
: npar(0), vector_size(0),
Lambda(NULL), sVec(NULL), oVec(NULL)
{
}
NormDecoder::~NormDecoder()
{
if (Lambda) Destroy();
}
bool NormDecoder::Init(int numParity, int vecSizeMax)
{
// Debugging assertions
ASSERT((numParity>=0)&&(numParity<=128));
ASSERT(vecSizeMax >= 0);
if (Lambda) Destroy(); // Check if already inited ...
npar = numParity;
vector_size = vecSizeMax;
if(!(Lambda = new unsigned char[2*npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for Lambda: %s\n",
strerror(errno));
return(false);
}
/* Allocate memory for sVec ptr and the syndrome vectors */
if(!(sVec = new unsigned char*[npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for sVec ptr: %s\n",
strerror(errno));
Destroy();
return(false);
}
int i;
for(i=0; i < npar; i++)
{
if(!(sVec[i] = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for new sVec: %s\n",
strerror(errno));
Destroy();
return(false);
}
}
/* Allocate memory for the oVec ptr and the Omega vectors */
if(!(oVec = new unsigned char*[npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for new oVec ptr: %s\n",
strerror(errno));
Destroy();
return(false);
}
for(i=0; i < npar; i++)
{
if(!(oVec[i] = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for new oVec: %s",
strerror(errno));
Destroy();
return(false);
}
}
if (!(scratch = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for scratch space: %s",
strerror(errno));
}
memset(scratch, 0, vecSizeMax*sizeof(unsigned char));
return(true);
} // end NormDecoder::Init()
void NormDecoder::Destroy()
{
if (scratch)
{
delete scratch;
scratch = NULL;
}
if(oVec)
{
for(int i=0; i<npar; i++)
if (oVec[i]) delete oVec[i];
delete oVec;
oVec = NULL;
}
if(sVec)
{
for(int i = 0; i < npar; i++)
if (sVec[i]) delete sVec[i];
delete sVec;
sVec = NULL;
}
if (Lambda)
{
delete Lambda;
Lambda = NULL;
}
} // end NormDecoder::Destroy()
// This will crash & burn if (erasureCount > npar)
int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* erasureLocs)
{
// Debugging assertions
ASSERT(Lambda);
ASSERT(erasureCount && (erasureCount<=npar));
#if defined(NS2) || defined (OPNET)
return erasureCount; // lobotomize FEC for faster simulations
#endif
// (A) Compute syndrome vectors
// First zero out erasure vectors (MDP provides zero-filled vecs)
// Then calculate syndrome (based on zero value erasures)
int nvecs = npar + ndata;
int vecSize = vector_size;
for (int i = 0; i < npar; i++)
{
int X = gexp(i+1);
unsigned char* synVec = sVec[i];
memset(synVec, 0, vecSize*sizeof(char));
for(int j = 0; j < nvecs; j++)
{
unsigned char* data = dVec[j] ? (unsigned char*)dVec[j] : scratch;
unsigned char* S = synVec;
for (int n = 0; n < vecSize; n++)
{
*S = *data++ ^ gmult(X, *S);
S++;
}
}
}
// (B) Init Lambda (the erasure locator polynomial)
int degree = 2*npar;
int nvecsMinusOne = nvecs - 1;
memset(Lambda, 0, degree*sizeof(char));
Lambda[0] = 1;
for (int i = 0; i < erasureCount; i++)
{
int X = gexp(nvecsMinusOne - erasureLocs[i]);
for(int j = (degree-1); j > 0; j--)
Lambda[j] = Lambda[j] ^ gmult(X, Lambda[j-1]);
}
// (C) Compute modified Omega using Lambda
for(int i = 0; i < npar; i++)
{
int k = i;
memset(oVec[i], 0, vecSize*sizeof(char));
int m = i + 1;
for(int j = 0; j < m; j++)
{
unsigned char* Omega = oVec[i];
unsigned char* S = sVec[j];
int Lk = Lambda[k--];
for(int n = 0; n < vecSize; n++)
*Omega++ ^= gmult(*S++, Lk);
}
}
// (D) Finally, fill in the erasures
for (int i = 0; i < erasureCount; i++)
{
// Only fill _data_ erasures
if (erasureLocs[i] >= ndata) return erasureCount;
// evaluate Lambda' (derivative) at alpha^(-i)
// ( all odd powers disappear)
int k = nvecsMinusOne - erasureLocs[i];
int denom = 0;
for (int j = 1; j < degree; j += 2)
denom ^= gmult(Lambda[j], gexp(((255-k)*(j-1)) % 255));
// Invert for use computing errror value below
denom = ginv(denom);
// Now evaluate Omega at alpha^(-i) (numerator)
unsigned char* eVec = (unsigned char*)dVec[erasureLocs[i]];
for (int j = 0; j < npar; j++)
{
unsigned char* data = eVec;
unsigned char* Omega = oVec[j];
int X = gexp(((255-k)*j) % 255);
for(int n = 0; n < vecSize; n++)
*data++ ^= gmult(*Omega++, X);
}
// Scale numerator with denominator
unsigned char* data = eVec;
for(int n = 0; n < vecSize; n++)
{
*data = gmult(*data, denom);
data++;
}
}
return erasureCount;
} // end NormDecoder::Decode()

86
common/normEncoder.h Normal file
View File

@ -0,0 +1,86 @@
/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
*
* (1) source code distributions retain this paragraph in its entirety,
*
* (2) distributions including binary code include this paragraph in
* its entirety in the documentation or other materials provided
* with the distribution, and
*
* (3) all advertising materials mentioning features or use of this
* software display the following acknowledgment:
*
* "This product includes software written and developed
* by Brian Adamson and Joe Macker of the Naval Research
* Laboratory (NRL)."
*
* The name of NRL, the name(s) of NRL employee(s), or any entity
* of the United States Government may not be used to endorse or
* promote products derived from this software, nor does the
* inclusion of the NRL written and developed software directly or
* indirectly suggest NRL or United States Government endorsement
* of this product.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
********************************************************************/
#ifndef _NORM_ENCODER
#define _NORM_ENCODER
#include "sysdefs.h" // protolib stuff
class NormEncoder
{
// Members
private:
int npar; // No. of parity packets (N-k)
int vecSize; // Size of biggest vector to encode
unsigned char* genPoly; // Ptr to generator polynomial
unsigned char* scratch; // scratch space for encoding
// Methods
public:
NormEncoder();
~NormEncoder();
bool Init(int numParity, int vectorSize);
void Destroy();
bool IsReady(){return (bool)(genPoly != NULL);}
void Encode(const char *dataVector, char **parityVectorList);
int NumParity() {return npar;}
int VectorSize() {return vecSize;}
private:
bool CreateGeneratorPolynomial();
};
class NormDecoder
{
// Members
private:
int npar; // No. of parity packets (n-K)
int vector_size; // Size of biggest vector to encode
unsigned char* Lambda; // Erasure location polynomial ("2*npar" ints)
unsigned char** sVec; // Syndrome vectors (pointers to "npar" vectors)
unsigned char** oVec; // Omega vectors (pointers to "npar" vectors)
unsigned char* scratch;
// Methods
public:
NormDecoder();
~NormDecoder();
bool Init(int numParity, int vectorSize);
int Decode(char **vectorList, int ndata, UINT16 erasureCount, UINT16* erasureLocs);
int NumParity() {return npar;}
int VectorSize() {return vector_size;}
void Destroy();
};
#endif // _NORM_ENCODER

453
common/normMessage.cpp Normal file
View File

@ -0,0 +1,453 @@
#include "normMessage.h"
#include "debug.h"
NormRepairRequest::NormRepairRequest()
: form(INVALID), flags(0), length(0), buffer(NULL), buffer_len(0)
{
}
bool NormRepairRequest::AppendRepairItem(const NormObjectId& objectId,
const NormBlockId& blockId,
UINT16 symbolId)
{
if (buffer_len >= (CONTENT_OFFSET+length+RepairItemLength()))
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2);
UINT32 temp32 =htonl((UINT32)blockId);
memcpy(buffer+length+CONTENT_OFFSET+2, &temp32, 4);
symbolId = htons(symbolId);
memcpy(buffer+length+CONTENT_OFFSET+6, &symbolId, 2);
length += RepairItemLength();
return true;
}
else
{
return false;
}
} // end NormRepairRequest::AppendRepairItem()
bool NormRepairRequest::AppendRepairRange(const NormObjectId& startObjectId,
const NormBlockId& startBlockId,
UINT16 startSymbolId,
const NormObjectId& endObjectId,
const NormBlockId& endBlockId,
UINT16 endSymbolId)
{
if (buffer_len >= (CONTENT_OFFSET+length+RepairRangeLength()))
{
// range start
UINT16 temp16;
temp16 = htons((UINT16)startObjectId);
memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2);
UINT32 temp32 =htonl((UINT32)startBlockId);
memcpy(buffer+length+CONTENT_OFFSET+2, &temp32, 4);
startSymbolId = htons(startSymbolId);
memcpy(buffer+length+CONTENT_OFFSET+6, &startSymbolId, 2);
// range end
temp16 = htons((UINT16)endObjectId);
memcpy(buffer+CONTENT_OFFSET+length+8, &temp16, 2);
temp32 =htonl((UINT32)endBlockId);
memcpy(buffer+length+CONTENT_OFFSET+10, &temp32, 4);
endSymbolId = htons(endSymbolId);
memcpy(buffer+length+CONTENT_OFFSET+14, &endSymbolId, 2);
length += RepairRangeLength();
return true;
}
else
{
return false;
}
} // end NormRepairRequest::AppendErasureCount()
bool NormRepairRequest::AppendErasureCount(const NormObjectId& objectId,
const NormBlockId& blockId,
UINT16 erasureCount)
{
if (buffer_len >= (CONTENT_OFFSET+length+ErasureItemLength()))
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2);
UINT32 temp32 =htonl((UINT32)blockId);
memcpy(buffer+length+CONTENT_OFFSET+2, &temp32, 4);
erasureCount = htons(erasureCount);
memcpy(buffer+length+CONTENT_OFFSET+6, &erasureCount, 2);
length += ErasureItemLength();
return true;
}
else
{
return false;
}
} // end NormRepairRequest::AppendErasureCount()
UINT16 NormRepairRequest::Pack()
{
if (length)
{
buffer[FORM_OFFSET] = form;
buffer[FLAGS_OFFSET] = (char)flags;
UINT16 temp16 = htons(length);
memcpy(buffer+LENGTH_OFFSET, &temp16, 2);
return (CONTENT_OFFSET + length);
}
else
{
return 0;
}
} // end NormRepairRequest::Pack()
UINT16 NormRepairRequest::Unpack()
{
// Make sure there's at least a header
if (buffer_len >= CONTENT_OFFSET)
{
form = (Form)buffer[FORM_OFFSET];
flags = (int)buffer[FLAGS_OFFSET];
memcpy(&length, buffer+LENGTH_OFFSET, 2);
length = ntohs(length);
if (length > (buffer_len - CONTENT_OFFSET))
{
// Badly formed message
return 0;
}
else
{
return (CONTENT_OFFSET+length);
}
}
else
{
return 0;
}
} // end NormRepairRequest::Unpack()
bool NormRepairRequest::RetrieveRepairItem(UINT16 offset,
NormObjectId* objectId,
NormBlockId* blockId,
UINT16* symbolId) const
{
if (length >= (offset + RepairItemLength()))
{
UINT16 temp16;
memcpy(&temp16, buffer+CONTENT_OFFSET+offset, 2);
*objectId = ntohs(temp16);
UINT32 temp32;
memcpy(&temp32, buffer+CONTENT_OFFSET+offset+2, 4);
*blockId = ntohl(temp32);
memcpy(symbolId, buffer+CONTENT_OFFSET+offset+6, 2);
*symbolId = ntohs(*symbolId);
return true;
}
else
{
return false;
}
} // end NormRepairRequest::RetrieveRepairItem()
NormRepairRequest::Iterator::Iterator(NormRepairRequest& theRequest)
: request(theRequest), offset(0)
{
}
// For erasure requests, symbolId is loaded with erasureCount
bool NormRepairRequest::Iterator::NextRepairItem(NormObjectId* objectId,
NormBlockId* blockId,
UINT16* symbolId)
{
if (request.RetrieveRepairItem(offset, objectId, blockId, symbolId))
{
offset += NormRepairRequest::RepairItemLength();
return true;
}
else
{
return false;
}
} // end NormRepairRequest::Iterator::NextRepairItem()
NormMessageQueue::NormMessageQueue()
: head(NULL), tail(NULL)
{
}
NormMessageQueue::~NormMessageQueue()
{
Destroy();
}
void NormMessageQueue::Destroy()
{
NormMessage* next;
while ((next = head))
{
head = next->next;
delete next;
}
} // end NormMessageQueue::Destroy()
void NormMessageQueue::Prepend(NormMessage* msg)
{
if ((msg->next = head))
head->prev = msg;
else
tail = msg;
msg->prev = NULL;
head = msg;
} // end NormMessageQueue::Prepend()
void NormMessageQueue::Append(NormMessage* msg)
{
if ((msg->prev = tail))
tail->next = msg;
else
head = msg;
msg->next = NULL;
tail = msg;
} // end NormMessageQueue::Append()
void NormMessageQueue::Remove(NormMessage* msg)
{
if (msg->prev)
msg->prev->next = msg->next;
else
head = msg->next;
if (msg->next)
msg->next->prev = msg->prev;
else
tail = msg->prev;
} // end NormMessageQueue::Remove()
NormMessage* NormMessageQueue::RemoveHead()
{
if (head)
{
NormMessage* msg = head;
if ((head = msg->next))
msg->next->prev = NULL;
else
tail = NULL;
return msg;
}
else
{
return NULL;
}
} // end NormMessageQueue::RemoveHead()
NormMessage* NormMessageQueue::RemoveTail()
{
if (tail)
{
NormMessage* msg = tail;
if ((tail = msg->prev))
msg->prev->next = NULL;
else
head = NULL;
return msg;
}
else
{
return NULL;
}
} // end NormMessageQueue::RemoveTail()
bool NormObjectSize::operator<(const NormObjectSize& size) const
{
UINT16 diff = msb - size.msb;
if (0 == diff)
return (lsb < size.lsb);
else if ((diff > 0x8000) ||
((0x8000 == diff) && (msb > size.msb)))
return true;
else
return false;
} // end NormObjectSize::operator<(NormObjectSize size)
bool NormObjectSize::operator>(const NormObjectSize& size) const
{
UINT16 diff = size.msb - msb;
if (0 == diff)
return (lsb > size.lsb);
else if ((diff > 0x8000) ||
((0x8000 == diff) && (size.msb > msb)))
return true;
else
return false;
} // end NormObjectSize::operator>(NormObjectSize id)
NormObjectSize NormObjectSize::operator+(const NormObjectSize& size) const
{
NormObjectSize total;
total.msb = msb + size.msb;
total.lsb = lsb + size.lsb;
if ((total.lsb < lsb) || (total.lsb < size.lsb)) total.msb++;
return total;
} // end NormObjectSize::operator+(NormObjectSize id)
NormObjectSize NormObjectSize::operator-(const NormObjectSize& b) const
{
NormObjectSize result;
result.lsb = lsb - b.lsb;
result.msb = msb - b.msb;
if (lsb < b.lsb) result.msb--;
return result;
} // end NormObjectSize::operator-(NormObjectSize id)
NormObjectSize NormObjectSize::operator*(const NormObjectSize& b) const
{
UINT32 ll = (lsb & 0x0000ffff) * (b.lsb & 0x0000ffff);
UINT32 lm = (lsb & 0x0000ffff) * ((b.lsb >> 16) & 0x0000ffff);
UINT32 ml = ((lsb >> 16) & 0x0000ffff) * (b.lsb & 0x0000ffff);
UINT32 lu = (lsb & 0x0000ffff) * (UINT32)b.msb;
UINT32 mm = ((lsb >> 16) & 0x0000ffff) *
((b.lsb >> 16) & 0x0000ffff);
NormObjectSize result;
result.lsb = ll + (lm << 16) + (ml << 16);
result.msb = lu + mm + ((lm >> 16) & 0x0000ffff) +
((ml >> 16) & 0x0000ffff);
return result;
} // end NormObjectSize::operator*(NormObjectSize size)
NormObjectSize NormObjectSize::operator/(const NormObjectSize& b) const
{
// Zero dividend is special case
if ((0 == lsb) && (0 == msb)) return NormObjectSize(0, 0);
// Zero divisor is special case
if ((0 == b.lsb) && (0 == b.msb)) return NormObjectSize(0xffff, 0xffffffff);
// Dividend equals divisor is special case
if (*this == b) return NormObjectSize(0,1);
// Divisor > dividend is special case
if ((b.lsb > lsb) && (b.msb > msb)) return NormObjectSize(0,1);
// divisor
UINT32 divisor[2];
divisor[0] = (UINT32)b.msb;
divisor[1] = b.lsb;
// dividend
UINT32 dividend[2];
dividend[0] = msb;
dividend[1] = (UINT32)lsb;
// remainder
UINT32 remainder[2];
remainder[0] = remainder[1] = 0;
unsigned int numBits = 64;
UINT32 d[2];
while (((divisor[1] > remainder[1]) && (divisor[0] == remainder[0])) ||
(divisor[0] > remainder[0]))
{
remainder[0] <<= 1;
remainder[0] |= (remainder[1] >> 31) & 0x00000001;
remainder[1] <<= 1;
remainder[1] |= (dividend[0] >> 31) & 0x00000001;
d[0] = dividend[0];
d[1] = dividend[1];
dividend[0] <<= 1;
dividend[0] |= (dividend[1] >> 31) & 0x00000001;
dividend[1] <<= 1;
numBits--;
}
// Note: above loop goes one step too far
// so we incr numBits, use old dividend (d)
numBits++;
UINT32 quotient[2];
quotient[0] = quotient[1] = 0;
bool extra = false;
for (unsigned int i = 0; i < numBits; i++)
{
UINT32 t[2];
t[0] = remainder[0] - divisor[0];
t[1] = remainder[1] - divisor[1];
if (remainder[1] < divisor[1]) t[0]--;
UINT32 q = ((t[0] >> 31) & 0x00000001) ^ 0x00000001;
d[0] <<= 1;
d[0] |= (d[1] >> 31) & 0x00000001;
d[1] <<= 1;
quotient[0] <<= 1;
quotient[1] = (quotient[1] << 1) | q;
if (q)
{
remainder[0] = t[0];
remainder[1] = t[1];
}
if (remainder[0] || remainder[1])
extra = true;
else
extra = false;
remainder[0] <<= 1;
remainder[0] |= (remainder[1] >> 31) & 0x00000001;
remainder[1] <<= 1;
remainder[1] |= (d[0] >> 31) & 0x00000001;
}
NormObjectSize result((UINT16)quotient[0], quotient[1]);
// If there was _any_ remainder, round up
if (extra) result++;
return result;
} // end NormObjectSize::operator/(NormObjectSize b)
bool NormObjectId::operator<(const NormObjectId& id) const
{
UINT16 diff = value - id.value;
if ((diff > 0x8000) ||
((0x8000 == diff) && (value > id.value)))
return true;
else
return false;
} // end NormObjectId::operator<(NormObjectId id)
bool NormObjectId::operator>(const NormObjectId& id) const
{
UINT16 diff = id.value - value;;
if ((diff > 0x8000) ||
((0x8000 == diff) && (id.value > value)))
return true;
else
return false;
} // end NormObjectId::operator>(NormObjectId id)
int NormObjectId::operator-(const NormObjectId& id) const
{
UINT16 diff = value - id.value;
if ((diff > 0x8000) ||
((0x8000 == diff) && (value > id.value)))
return ((INT16)(value - id.value));
else
return (-(INT16)(id.value - value));
} // end NormObjectId::operator-(NormObjectId id) const
bool NormBlockId::operator<(const NormBlockId& id) const
{
UINT32 diff = value - id.value;
if ((diff > 0x80000000) ||
((0x80000000 == diff) && (value > id.value)))
return true;
else
return false;
} // end NormBlockId::operator<(NormBlockId id)
bool NormBlockId::operator>(const NormBlockId& id) const
{
UINT32 diff = id.value - value;;
if ((diff > 0x80000000) ||
((0x80000000 == diff) && (id.value > value)))
return true;
else
return false;
} // end NormBlockId::operator>(NormBlockId id)
long NormBlockId::operator-(const NormBlockId& id) const
{
UINT32 diff = value - id.value;
if ((diff > 0x80000000) ||
((0x80000000 == diff) && (value > id.value)))
return ((INT32)(value - id.value));
else
return (-(INT32)(id.value - value));
} // end NormBlockId::operator-(NormBlockId id) const

975
common/normMessage.h Normal file
View File

@ -0,0 +1,975 @@
#ifndef _NORM_MESSAGE
#define _NORM_MESSAGE
// PROTOLIB includes
#include <networkAddress.h> // for NetworkAddress class
#include <sysdefs.h> // for UINT typedefs
#include <debug.h>
// standard includes
#include <string.h> // for memcpy(), etc
// (TBD) Alot of the "memcpy()" calls could be eliminated by
// taking advantage of the alignment of NORM messsages
const UINT16 NORM_MSG_SIZE_MAX = 8192;
const int NORM_ROBUST_FACTOR = 20; // default robust factor
// 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;
bool operator>(const NormObjectSize& size) const;
bool operator==(const NormObjectSize& size) const
{return ((msb == size.msb) && (lsb == size.lsb));}
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;
class NormObjectId
{
public:
NormObjectId() {};
NormObjectId(UINT16 id) {value = id;}
NormObjectId(const NormObjectId& id) {value = id.value;}
operator UINT16() const {return value;}
bool operator<(const NormObjectId& id) const;
bool operator>(const NormObjectId& id) const;
bool operator==(const NormObjectId& id) const
{return (value == id.value);}
bool operator!=(const NormObjectId& id) const
{return (value != id.value);}
int operator-(const NormObjectId& id) const;
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;
bool operator>(const NormBlockId& id) const;
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;
NormBlockId& operator++(int ) {value++; return *this;}
private:
UINT32 value;
}; // end class NormObjectId
typedef UINT16 NormSymbolId;
typedef NormSymbolId NormSegmentId;
const NormNodeId NORM_NODE_INVALID = 0x0;
const NormNodeId NORM_NODE_ANY = 0xffffffff;
enum NormMsgType
{
NORM_MSG_INVALID,
NORM_MSG_INFO,
NORM_MSG_DATA,
NORM_MSG_CMD,
NORM_MSG_NACK,
NORM_MSG_ACK,
NORM_MSG_REPORT
};
class NormMsg
{
public:
// Message building routines
void SetVersion(UINT8 version) {buffer[VERSION_OFFSET] = version;}
void SetType(NormMsgType type) {buffer[TYPE_OFFSET] = type;}
void SetSequence(UINT16 sequence)
{
UINT16 temp16 = htons(sequence);
memcpy(buffer+SEQUENCE_OFFSET, &temp16, 2);
}
void SetSender(NormNodeId sender)
{
UINT32 temp32 = htonl(sender);
memcpy(buffer+SENDER_OFFSET, &temp32, 4);
}
void SetDestination(const NetworkAddress& dest)
{addr = dest;}
// Message processing routines
UINT8 GetVersion() const {return buffer[VERSION_OFFSET];}
NormMsgType GetType() const {return (NormMsgType)buffer[TYPE_OFFSET];}
UINT16 GetSequence() const
{
UINT16 temp16;
memcpy(&temp16, buffer+SEQUENCE_OFFSET, 2);
return (ntohs(temp16));
}
NormNodeId GetSender() const
{
UINT32 temp32;
memcpy(&temp32, buffer+SENDER_OFFSET, 4);
return (ntohl(temp32));
}
// For message buffer transmission/reception and misc.
NetworkAddress* Src() {return &addr;}
NetworkAddress* Dest() {return &addr;}
char* GetBuffer() {return buffer;}
UINT16 GetLength() const {return length;}
void SetLength(UINT16 len) {length = len;}
protected:
// Common message header offsets
enum
{
VERSION_OFFSET = 0,
TYPE_OFFSET = VERSION_OFFSET + 1,
SEQUENCE_OFFSET = TYPE_OFFSET + 1,
SENDER_OFFSET = SEQUENCE_OFFSET + 2,
MSG_OFFSET = SENDER_OFFSET + 4
};
char buffer[NORM_MSG_SIZE_MAX];
UINT16 length;
NetworkAddress addr; // src/dst (default dst is session address)
}; // end class NormMsg
// "NormObjectMsg" are comprised of NORM_INFO and NORM_DATA messages
// (NormInfoMsg and NormDataMsg will derive from this type)
class NormObjectMsg : public NormMsg
{
public:
enum Flag
{
FLAG_REPAIR = 0x01,
FLAG_EXPLICIT = 0x02,
FLAG_INFO = 0x04,
FLAG_UNRELIABLE = 0x08,
FLAG_FILE = 0x10,
FLAG_STREAM = 0x20
};
bool FlagIsSet(NormObjectMsg::Flag flag) const
{return (0 != (flag & buffer[FLAGS_OFFSET]));}
UINT8 GetGrtt() const {return buffer[GRTT_OFFSET];}
UINT8 GetGroupSize() const {return buffer[GSIZE_OFFSET];}
UINT8 GetFecId() const {return buffer[FEC_ID_OFFSET];}
UINT16 GetSegmentSize() const
{
UINT16 segmentSize;
memcpy(&segmentSize, buffer+SEG_SIZE_OFFSET, 2);
return (ntohs(segmentSize));
}
NormObjectSize GetObjectSize() const
{
UINT16 temp16;
memcpy(&temp16, buffer+OBJ_SIZE_OFFSET, 2);
UINT32 temp32;
memcpy(&temp32, buffer+OBJ_SIZE_OFFSET+2, 4);
return NormObjectSize(ntohs(temp16), ntohl(temp32));
}
UINT16 GetFecEncodingName() const
{
UINT16 encodingName;
memcpy(&encodingName, buffer+FEC_NAME_OFFSET, 2);
return (ntohs(encodingName));
}
UINT16 GetFecNumParity() const
{
UINT16 nparity;
memcpy(&nparity, buffer+FEC_NPARITY_OFFSET, 2);
return (ntohs(nparity));
}
UINT16 GetFecBlockLen() const
{
UINT16 nparity;
memcpy(&nparity, buffer+FEC_NDATA_OFFSET, 2);
return (ntohs(nparity));
}
NormObjectId GetObjectId() const
{
UINT16 temp16;
memcpy(&temp16, buffer+OBJ_ID_OFFSET, 2);
return (ntohs(temp16));
}
// Message building routines
void SetFlag(NormObjectMsg::Flag flag)
{buffer[FLAGS_OFFSET] |= flag;}
void SetGrtt(UINT8 grtt) {buffer[GRTT_OFFSET] = grtt;}
void SetGroupSize(UINT8 gsize) {buffer[GSIZE_OFFSET] = gsize;}
void SetFecId(UINT8 fecId) {buffer[FEC_ID_OFFSET] = fecId;}
void SetSegmentSize(UINT16 segmentSize)
{
segmentSize = htons(segmentSize);
memcpy(buffer+SEG_SIZE_OFFSET, &segmentSize, 2);
}
void SetObjectSize(const NormObjectSize& objectSize)
{
UINT16 temp16 = htons(objectSize.MSB());
memcpy(buffer+OBJ_SIZE_OFFSET, &temp16, 2);
UINT32 temp32 = htonl(objectSize.LSB());
memcpy(buffer+OBJ_SIZE_OFFSET+2, &temp32, 4);
}
void SetFecEncodingName(UINT16 encodingName)
{
encodingName = htons(encodingName);
memcpy(buffer+FEC_NAME_OFFSET, &encodingName, 2);
}
void SetFecNumParity(UINT16 nparity)
{
nparity = htons(nparity);
memcpy(buffer+FEC_NPARITY_OFFSET, &nparity, 2);
}
void SetFecBlockLen(UINT16 blockLen)
{
blockLen = htons(blockLen);
memcpy(buffer+FEC_NDATA_OFFSET, &blockLen, 2);
}
void SetObjectId(const NormObjectId& objectId)
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+OBJ_ID_OFFSET, &temp16, 2);
}
protected:
enum
{
FLAGS_OFFSET = MSG_OFFSET,
GRTT_OFFSET = FLAGS_OFFSET + 1,
GSIZE_OFFSET = GRTT_OFFSET + 1,
FEC_ID_OFFSET = GSIZE_OFFSET + 1,
SEG_SIZE_OFFSET = FEC_ID_OFFSET + 1,
OBJ_SIZE_OFFSET = SEG_SIZE_OFFSET + 2,
RESV_OFFSET = OBJ_SIZE_OFFSET + 6,
FEC_NAME_OFFSET = RESV_OFFSET + 2,
FEC_NPARITY_OFFSET = FEC_NAME_OFFSET + 2,
FEC_NDATA_OFFSET = FEC_NPARITY_OFFSET + 2,
OBJ_ID_OFFSET = FEC_NDATA_OFFSET + 2
};
}; // end class NormObjectMsg
class NormInfoMsg : public NormObjectMsg
{
public:
UINT16 GetInfoLen() const {return (length - INFO_OFFSET);}
const char* GetInfo() const {return (buffer + INFO_OFFSET);}
void SetInfo(char* data, UINT16 len)
{
memcpy(buffer+INFO_OFFSET, data, len);
length = len + INFO_OFFSET;
}
private:
enum
{
INFO_OFFSET = OBJ_ID_OFFSET + 2
};
}; // end class NormInfoMsg
class NormDataMsg : public NormObjectMsg
{
public:
// Message building methods
void SetFecBlockId(const NormBlockId& blockId)
{
UINT32 temp32 = htonl((UINT32)blockId);
memcpy(buffer+BLOCK_ID_OFFSET, &temp32, 4);
}
void SetFecSymbolId(UINT16 symbolId)
{
symbolId = htons(symbolId);
memcpy(buffer+SYMBOL_ID_OFFSET, &symbolId, 2);
}
char* AccessData() {return (buffer+DATA_OFFSET);}
void SetData(const NormObjectSize& offset, char* data, UINT16 len)
{
WriteLength(buffer+LENGTH_OFFSET, len);
WriteOffset(buffer+LENGTH_OFFSET, offset);
memcpy(buffer+DATA_OFFSET, data, len);
length = DATA_OFFSET + len;
}
void SetDataOffset(const NormObjectSize& offset)
{
WriteOffset(buffer+LENGTH_OFFSET, offset);
}
void SetDataLength(UINT16 len)
{
WriteLength(buffer+LENGTH_OFFSET, len);
length = DATA_OFFSET + len;
}
// (Note: "payload_len" and "offset" field spaces are in the FEC payload)
char* AccessPayload() {return (buffer+PAYLOAD_OFFSET);}
void SetPayload(const char* data, UINT16 len)
{
memcpy(buffer+PAYLOAD_OFFSET, data, len);
length = PAYLOAD_OFFSET + len;
}
// Message processing methods
NormBlockId GetFecBlockId() const
{
UINT32 temp32;
memcpy(&temp32, buffer+BLOCK_ID_OFFSET, 4);
return (ntohl(temp32));
}
UINT16 GetFecSymbolId() const
{
UINT16 temp16;
memcpy(&temp16, buffer+SYMBOL_ID_OFFSET, 2);
return (ntohs(temp16));
}
bool IsData() const {return (GetFecSymbolId() < GetFecBlockLen());}
const char* GetData() {return (buffer + DATA_OFFSET);}
UINT16 GetDataLen() const {return (length - DATA_OFFSET);}
const char* GetPayload() {return (buffer+LENGTH_OFFSET);}
UINT16 GetPayloadLen() const {return (length - LENGTH_OFFSET);}
bool IsParity() const {return (GetFecSymbolId() >= GetFecBlockLen());}
// (Note: "payload_len" and "offset" field spaces are in the FEC payload)
char* GetParity() {return buffer+LENGTH_OFFSET;}
// (Note: This should be the same as "segment_size")
UINT16 GetParityLen() const
{return (length - LENGTH_OFFSET);}
// Some static helper routines for various purposes
static UINT16 PayloadHeaderLen() {return (DATA_OFFSET - LENGTH_OFFSET);}
static void WriteLength(char* payload, UINT16 len)
{
UINT16 temp16 = htons(len);
memcpy(payload+LENGTH_OFFSET-LENGTH_OFFSET, &temp16, 2);
}
static void WriteOffset(char* payload, const NormObjectSize& offset)
{
UINT16 temp16 = htons(offset.MSB());
memcpy(payload+OFFSET_OFFSET-LENGTH_OFFSET, &temp16, 2);
UINT32 temp32 = htonl(offset.LSB());
memcpy(payload+OFFSET_OFFSET-LENGTH_OFFSET+2, &temp32, 4);
}
static UINT16 ReadLength(const char* payload)
{
UINT16 temp16;
memcpy(&temp16, payload+LENGTH_OFFSET-LENGTH_OFFSET, 2);
return ntohs(temp16);
}
static NormObjectSize ReadOffset(const char* payload)
{
UINT16 temp16;
memcpy(&temp16, payload+OFFSET_OFFSET-LENGTH_OFFSET, 2);
UINT32 temp32;
memcpy(&temp32, payload+OFFSET_OFFSET-LENGTH_OFFSET+2, 4);
return NormObjectSize(ntohs(temp16), ntohl(temp32));
}
private:
enum
{
BLOCK_ID_OFFSET = OBJ_ID_OFFSET + 2,
SYMBOL_ID_OFFSET = BLOCK_ID_OFFSET + 4,
PAYLOAD_OFFSET = SYMBOL_ID_OFFSET + 2,
LENGTH_OFFSET = PAYLOAD_OFFSET,
OFFSET_OFFSET = LENGTH_OFFSET + 2,
DATA_OFFSET = OFFSET_OFFSET + 6
};
}; // end class NormDataMsg
class NormCmdMsg : public NormMsg
{
public:
enum Flavor
{
NORM_CMD_INVALID,
NORM_CMD_FLUSH,
NORM_CMD_SQUELCH,
NORM_CMD_ACK_REQ,
NORM_CMD_REPAIR_ADV,
NORM_CMD_CC,
NORM_CMD_APPLICATION
};
// Message building
void SetGrtt(UINT8 quantizedGrtt)
{buffer[GRTT_OFFSET] = quantizedGrtt;}
void SetGroupSize(UINT8 quantizedGroupSize)
{buffer[GSIZE_OFFSET] = quantizedGroupSize;}
void SetFlavor(NormCmdMsg::Flavor flavor)
{buffer[FLAVOR_OFFSET] = flavor;}
// Message processing
UINT8 GetGrtt() {return buffer[GRTT_OFFSET];}
UINT8 GetGroupSize() {return buffer[GSIZE_OFFSET];}
NormCmdMsg::Flavor GetFlavor() {return (Flavor)buffer[FLAVOR_OFFSET];}
protected:
enum
{
GRTT_OFFSET = MSG_OFFSET,
GSIZE_OFFSET = GRTT_OFFSET + 1,
FLAVOR_OFFSET = GSIZE_OFFSET + 1
};
}; // end class NormCmdMsg
class NormCmdFlushMsg : public NormCmdMsg
{
public:
enum Flag {NORM_FLUSH_FLAG_EOT = 0x01};
// Message building
void SetFlag(NormCmdFlushMsg::Flag flag)
{buffer[FLAGS_OFFSET] |= flag;}
void UnsetFlag(NormCmdFlushMsg::Flag flag)
{buffer[FLAGS_OFFSET] &= ~flag;}
void SetObjectId(const NormObjectId& objectId)
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+OBJECT_ID_OFFSET, &temp16, 2);
}
void SetFecBlockId(const NormBlockId& blockId)
{
UINT32 temp32 = htonl((UINT32)blockId);
memcpy(buffer+BLOCK_ID_OFFSET, &temp32, 4);
}
void SetFecSymbolId(UINT16 symbolId)
{
symbolId = htons(symbolId);
memcpy(buffer+SYMBOL_ID_OFFSET, &symbolId, 2);
}
// Message processing
bool FlagIsSet(NormCmdFlushMsg::Flag flag)
{return (0 != (buffer[FLAGS_OFFSET] & flag));}
NormObjectId GetObjectId()
{
UINT16 temp16;
memcpy(&temp16, buffer+OBJECT_ID_OFFSET, 2);
return (ntohs(temp16));
}
NormBlockId GetFecBlockId()
{
UINT32 temp32;
memcpy(&temp32, buffer+BLOCK_ID_OFFSET, 4);
return (ntohl(temp32));
}
UINT16 GetFecSymbolId()
{
UINT16 temp16;
memcpy(&temp16, buffer+SYMBOL_ID_OFFSET, 2);
return (ntohs(temp16));
}
// Flush messages are fixed length
UINT16 GetLength() {return (SYMBOL_ID_OFFSET + 2);}
private:
enum
{
FLAGS_OFFSET = FLAVOR_OFFSET + 1,
OBJECT_ID_OFFSET = FLAGS_OFFSET + 1,
BLOCK_ID_OFFSET = OBJECT_ID_OFFSET + 2,
SYMBOL_ID_OFFSET = BLOCK_ID_OFFSET + 4
};
}; // end class NormCmdFlushMsg
class NormCmdSquelchMsg : public NormCmdMsg
{
public:
// Message building
void SetObjectId(NormObjectId objectId)
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+OBJECT_ID_OFFSET, &temp16, 2);
}
void SetFecBlockId(NormBlockId blockId)
{
UINT32 temp32 = htonl((UINT32)blockId);
memcpy(buffer+BLOCK_ID_OFFSET, &temp32, 4);
}
void SetFecSymbolId(UINT16 symbolId)
{
symbolId = htons(symbolId);
memcpy(buffer+SYMBOL_ID_OFFSET, &symbolId, 2);
}
void ResetInvalidObjectList() {length = OBJECT_LIST_OFFSET;}
UINT16 AppendInvalidObject(NormObjectId objectId)
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+length, &temp16, 2);
length += 2;
return length;
}
// Message processing
NormObjectId GetObjectId()
{
UINT16 temp16;
memcpy(&temp16, buffer+OBJECT_ID_OFFSET, 2);
return (ntohs(temp16));
}
NormBlockId GetFecBlockId()
{
UINT32 temp32;
memcpy(&temp32, buffer+BLOCK_ID_OFFSET, 4);
return (NormBlockId(ntohl(temp32)));
}
UINT16 GetFecSymbolId()
{
UINT16 temp16;
memcpy(&temp16, buffer+SYMBOL_ID_OFFSET, 2);
return (ntohs(temp16));
}
// Use these to parse invalid object list
UINT16 GetInvalidObjectCount()
{return ((length - OBJECT_LIST_OFFSET) / 2);}
NormObjectId GetInvalidObjectId(UINT16 index)
{
UINT16 temp16;
memcpy(&temp16, buffer + (2*index+OBJECT_LIST_OFFSET), 2);
return (ntohs(temp16));
}
private:
enum
{
RESERVED_OFFSET = FLAVOR_OFFSET + 1,
OBJECT_ID_OFFSET = RESERVED_OFFSET + 1,
BLOCK_ID_OFFSET = OBJECT_ID_OFFSET + 2,
SYMBOL_ID_OFFSET = BLOCK_ID_OFFSET + 4,
OBJECT_LIST_OFFSET = SYMBOL_ID_OFFSET + 2,
};
}; // end class NormCmdSquelchMsg
class NormCmdAckReqMsg : public NormCmdMsg
{
public:
// Note: Support for application-defined AckFlavors (32-255)
// may be provided, but 0-31 are reserved values
enum AckFlavor
{
INVALID = 0,
WATERMARK = 1,
RTT = 2,
APP_BASE = 32
};
// Message building
void SetAckFlavor(UINT8 ackFlavor)
{buffer[ACK_FLAVOR_OFFSET] = ackFlavor;}
// For setting generic content
void SetAckContent(char content[64])
{memcpy(buffer+CONTENT_OFFSET, content, 64);}
void ResetAckingNodeList()
{length = NODE_LIST_OFFSET;}
UINT16 AppendAckingNode(NormNodeId nodeId)
{
nodeId = htonl(nodeId);
memcpy(buffer+length, &nodeId, 4);
return length;
}
void SetWatermark(NormObjectId objectId,
NormBlockId fecBlockId,
UINT16 fecSymbolId)
{
UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+OBJECT_ID_OFFSET, &temp16, 2);
UINT32 temp32 = htonl((UINT32)fecBlockId);
memcpy(buffer+BLOCK_ID_OFFSET, &temp32, 4);
fecSymbolId = htons(fecSymbolId);
memcpy(buffer+SYMBOL_ID_OFFSET, &fecSymbolId, 2);
}
void SetRttSendTime(const struct timeval& sendTime)
{
UINT32 temp32 = htonl(sendTime.tv_sec);
memcpy(buffer+SEND_TIME_OFFSET, &temp32, 4);
temp32 = htonl(sendTime.tv_usec);
memcpy(buffer+SEND_TIME_OFFSET+4, &temp32, 4);
}
// Message processing
NormCmdAckReqMsg::AckFlavor GetAckFlavor()
{return (AckFlavor)buffer[ACK_FLAVOR_OFFSET];}
char* GetAckContent() {return (buffer+CONTENT_OFFSET);}
UINT16 GetAckingNodeCount()
{return ((length - NODE_LIST_OFFSET) / 4);}
NormNodeId GetAckingNodeId(UINT16 index)
{
UINT32 temp32;
memcpy(&temp32, buffer+NODE_LIST_OFFSET+(4*index), 4);
return (ntohl(temp32));
}
void GetWatermark(NormObjectId& objectId,
NormBlockId& fecBlockId,
UINT16 fecSymbolId)
{
UINT16 temp16;
memcpy(&temp16, buffer+OBJECT_ID_OFFSET, 2);
objectId = ntohs(temp16);
UINT32 temp32;
memcpy(&temp32, buffer+BLOCK_ID_OFFSET, 4);
fecBlockId = ntohl(temp32);
memcpy(&fecSymbolId, buffer+SYMBOL_ID_OFFSET, 2);
fecSymbolId = ntohs(fecSymbolId);
}
void GetRttSendTime(struct timeval& sendTime)
{
memcpy(&sendTime.tv_sec, buffer+SEND_TIME_OFFSET, 4);
sendTime.tv_sec = ntohl(sendTime.tv_sec);
memcpy(&sendTime.tv_usec, buffer+SEND_TIME_OFFSET+4, 4);
sendTime.tv_usec = ntohl(sendTime.tv_usec);
}
private:
enum GenericOffsets
{
ACK_FLAVOR_OFFSET = FLAVOR_OFFSET + 1,
CONTENT_OFFSET = ACK_FLAVOR_OFFSET + 1,
NODE_LIST_OFFSET = CONTENT_OFFSET + 8
};
enum WatermarkOffsets
{
OBJECT_ID_OFFSET = CONTENT_OFFSET,
BLOCK_ID_OFFSET = OBJECT_ID_OFFSET + 2,
SYMBOL_ID_OFFSET = BLOCK_ID_OFFSET + 4
};
enum RttOffsets
{
SEND_TIME_OFFSET = CONTENT_OFFSET
};
}; // end class NormCmdAckReqMsg
class NormCmdRepairAdvMsg : public NormCmdMsg
{
public:
enum Flag {NORM_REPAIR_ADV_FLAG_LIMIT = 0x01};
// TBD (Uses NormNack format)
}; // end class NormCmdRepairAdvMsg
class NormCmdCCMsg : public NormCmdMsg
{
// (TBD) Congestion control command message
}; // end class NormCmdCCMsg
class NormCmdApplicationMsg : public NormCmdMsg
{
// (TBD) Application-defined command (non-acked)
}; // end class NormCmdApplicationMsg
class NormRepairRequest
{
friend class NormRepairRequest::Iterator;
public:
enum Form
{
INVALID,
ITEMS,
RANGES,
ERASURES
};
enum Flag
{
SEGMENT = 0x01,
BLOCK = 0x02,
INFO = 0x04,
OBJECT = 0x08
};
// Construction
NormRepairRequest();
void SetBuffer(char* bufferPtr, UINT16 bufferLen)
{
buffer = bufferPtr;
buffer_len = bufferLen;
length = 0;
}
static UINT16 RepairItemLength() {return 8;}
static UINT16 RepairRangeLength() {return 16;}
static UINT16 ErasureItemLength() {return 8;}
// Repair request building
void SetForm(NormRepairRequest::Form theForm) {form = theForm;}
void SetFlag(NormRepairRequest::Flag theFlag) {flags |= theFlag;}
void SetFlags(int theFlags) {flags |= theFlags;}
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 symbolId);
bool AppendRepairRange(const NormObjectId& startObjectId,
const NormBlockId& startBlockId,
UINT16 startSymbolId,
const NormObjectId& endObjectId,
const NormBlockId& endBlockId,
UINT16 endSymbolId);
bool AppendErasureCount(const NormObjectId& objectId,
const NormBlockId& blockId,
UINT16 erasureCount);
UINT16 Pack();
// Repair request processing
UINT16 Unpack();
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* symbolId);
private:
NormRepairRequest& request;
UINT16 offset;
}; // end class NormRepairRequest::Iterator
private:
bool RetrieveRepairItem(UINT16 offset,
NormObjectId* objectId,
NormBlockId* blockId,
UINT16* erasureCount) const;
enum
{
FORM_OFFSET = 0,
FLAGS_OFFSET = FORM_OFFSET + 1,
LENGTH_OFFSET = FLAGS_OFFSET + 1,
CONTENT_OFFSET = LENGTH_OFFSET + 2
};
Form form;
int flags;
UINT16 length;
char* buffer;
UINT16 buffer_len;
}; // end class NormRepairRequest
class NormNackMsg : public NormMsg
{
public:
// Message building
void SetServerId(NormNodeId serverId)
{
serverId = htonl(serverId);
memcpy(buffer+SERVER_ID_OFFSET, &serverId, 4);
}
void SetGrttResponse(const struct timeval& grttResponse)
{
UINT32 temp32 = htonl(grttResponse.tv_sec);
memcpy(buffer+GRTT_RESPONSE_OFFSET, &temp32, 4);
temp32 = htonl(grttResponse.tv_usec);
memcpy(buffer+GRTT_RESPONSE_OFFSET+4, &temp32, 4);
}
void SetLossEstimate(UINT16 lossEstimate)
{
lossEstimate = htons(lossEstimate);
memcpy(buffer+LOSS_OFFSET, &lossEstimate, 2);
}
void SetGrttSequence(UINT16 sequence)
{
sequence = htons(sequence);
memcpy(buffer+GRTT_SEQUENCE_OFFSET, &sequence, 2);
}
void ResetNackContent() {length = CONTENT_OFFSET;}
void AttachRepairRequest(NormRepairRequest& request,
UINT16 segmentMax)
{
int buflen = segmentMax - (length - CONTENT_OFFSET);
buflen = (buflen>0) ? buflen : 0;
request.SetBuffer(buffer+length, buflen);
}
UINT16 PackRepairRequest(NormRepairRequest& request)
{
UINT16 requestLength = request.Pack();
length += request.Pack();
return requestLength;
}
// Message processing
NormNodeId GetServerId()
{
UINT32 temp32;
memcpy(&temp32, buffer+SERVER_ID_OFFSET, 4);
return (ntohl(temp32));
}
void GetGrttResponse(struct timeval& grttResponse)
{
memcpy(&grttResponse.tv_sec, buffer+GRTT_RESPONSE_OFFSET, 4);
grttResponse.tv_sec = ntohl(grttResponse.tv_sec);
memcpy(&grttResponse.tv_usec, buffer+GRTT_RESPONSE_OFFSET+4, 4);
grttResponse.tv_usec = ntohl(grttResponse.tv_usec);
}
UINT16 GetLossEstimate()
{
UINT16 temp16;
memcpy(&temp16, buffer+LOSS_OFFSET, 2);
return (ntohs(temp16));
}
UINT16 GetGrttSequence()
{
UINT16 temp16;
memcpy(&temp16, buffer+GRTT_SEQUENCE_OFFSET, 2);
return (ntohs(temp16));
}
UINT16 UnpackRepairRequest(NormRepairRequest& request,
UINT16 requestOffset)
{
int buflen = length - CONTENT_OFFSET - requestOffset;
buflen = (buflen > 0) ? buflen : 0;
request.SetBuffer(buffer + CONTENT_OFFSET + requestOffset,
buflen);
return request.Unpack();
}
private:
enum
{
SERVER_ID_OFFSET = MSG_OFFSET,
GRTT_RESPONSE_OFFSET = SERVER_ID_OFFSET + 4,
LOSS_OFFSET = GRTT_RESPONSE_OFFSET + 8,
GRTT_SEQUENCE_OFFSET = LOSS_OFFSET + 2,
CONTENT_OFFSET = GRTT_SEQUENCE_OFFSET + 2
};
}; // end class NormNackMsg
class NormAckMsg : public NormMsg
{
// (TBD)
}; // 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 NormCommandMessage
{
public:
union
{
NormCmdMsg generic;
NormCmdFlushMsg flush;
NormCmdSquelchMsg squelch;
NormCmdAckReqMsg ack_req;
NormCmdRepairAdvMsg repair_adv;
NormCmdCCMsg cc;
NormCmdApplicationMsg app;
};
}; // end class NormCommandMessage
class NormMessage
{
friend class NormMessageQueue;
public:
union
{
NormMsg generic;
NormObjectMsg object;
NormInfoMsg info;
NormDataMsg data;
NormCommandMessage cmd;
NormNackMsg nack;
NormAckMsg ack;
NormReportMsg report;
};
private:
NormMessage* prev;
NormMessage* next;
}; // end class NormMessage
class NormMessageQueue
{
public:
NormMessageQueue();
~NormMessageQueue();
void Destroy();
void Prepend(NormMessage* msg);
void Append(NormMessage* msg);
void Remove(NormMessage* msg);
NormMessage* RemoveHead();
NormMessage* RemoveTail();
bool IsEmpty() {return ((NormMessage*)NULL == head);}
private:
NormMessage* head;
NormMessage* tail;
}; // end class NormMessageQueue
#endif // _NORM_MESSAGE

843
common/normNode.cpp Normal file
View File

@ -0,0 +1,843 @@
#include "normNode.h"
#include "normSession.h"
#include <errno.h>
NormNode::NormNode(class NormSession* theSession, NormNodeId nodeId)
: session(theSession), id(nodeId),
parent(NULL), right(NULL), left(NULL),
prev(NULL), next(NULL)
{
}
NormNode::~NormNode()
{
}
const NormNodeId& NormNode::LocalNodeId() {return session->LocalNodeId();}
NormServerNode::NormServerNode(class NormSession* theSession, NormNodeId nodeId)
: NormNode(theSession, nodeId), synchronized(false),
is_open(false), segment_size(0), ndata(0), nparity(0), erasure_loc(NULL)
{
repair_timer.Init(0.0, -1, (ProtocolTimerOwner*)this,
(ProtocolTimeoutFunc)&NormServerNode::OnRepairTimeout);
}
NormServerNode::~NormServerNode()
{
Close();
}
bool NormServerNode::Open(UINT16 segmentSize, UINT16 numData, UINT16 numParity)
{
if (!rx_table.Init(256))
{
DMSG(0, "NormServerNode::Open() rx_table init error\n");
Close();
return false;
}
if (!rx_pending_mask.Init(256))
{
DMSG(0, "NormServerNode::Open() rx_pending_mask init error\n");
Close();
return false;
}
if (!rx_repair_mask.Init(256))
{
DMSG(0, "NormServerNode::Open() rx_repair_mask init error\n");
Close();
return false;
}
// Calculate how much memory each buffered block will require
UINT16 blockSize = numData + numParity;
unsigned long maskSize = blockSize >> 3;
if (0 != (blockSize & 0x07)) maskSize++;
unsigned long blockSpace = sizeof(NormBlock) +
blockSize * sizeof(char*) +
2*maskSize +
numData * (segmentSize + NormDataMsg::PayloadHeaderLen());
unsigned long bufferSpace = session->RemoteServerBufferSize();
unsigned long numBlocks = bufferSpace / blockSpace;
if (bufferSpace > (numBlocks*blockSpace)) numBlocks++;
if (numBlocks < 2) numBlocks = 2;
unsigned long numSegments = numBlocks * numData;
if (!block_pool.Init(numBlocks, blockSize))
{
DMSG(0, "NormServerNode::Open() block_pool init error\n");
Close();
return false;
}
if (!segment_pool.Init(numSegments, segmentSize+NormDataMsg::PayloadHeaderLen()))
{
DMSG(0, "NormServerNode::Open() segment_pool init error\n");
Close();
return false;
}
if (!decoder.Init(numParity, segmentSize+NormDataMsg::PayloadHeaderLen()))
{
DMSG(0, "NormServerNode::Open() decoder init error: %s\n",
strerror(errno));
Close();
return false;
}
if (!(erasure_loc = new UINT16[numParity]))
{
DMSG(0, "NormServerNode::Open() erasure_loc allocation error: %s\n",
strerror(errno));
Close();
return false;
}
segment_size = segmentSize;
ndata = numData;
nparity = numParity;
is_open= true;
return true;
} // end NormServerNode::Open()
void NormServerNode::Close()
{
decoder.Destroy();
if (erasure_loc)
{
delete []erasure_loc;
erasure_loc = NULL;
}
NormObjectTable::Iterator iterator(rx_table);
NormObject* obj;
while ((obj = iterator.GetNextObject()))
{
// (TBD) Notify app of object closing
obj->Close();
delete obj;
}
rx_repair_mask.Destroy();
rx_pending_mask.Destroy();
rx_table.Destroy();
segment_size = ndata = nparity = 0;
is_open = false;
} // end NormServerNode::Close()
void NormServerNode::HandleObjectMessage(NormMessage& msg)
{
if (IsOpen())
{
// (TBD - also verify encoder name ...)
if ((msg.object.GetSegmentSize() != segment_size) &&
(ndata != msg.object.GetFecBlockLen()) &&
(nparity != msg.object.GetFecNumParity()))
{
DMSG(0, "NormServerNode::HandleObjectMessage() node:%lu remote server:%lu parameter change.\n",
LocalNodeId(), Id());
Close();
if (!Open(msg.object.GetSegmentSize(), msg.object.GetFecBlockLen(),
msg.object.GetFecNumParity()))
{
DMSG(0, "NormServerNode::HandleObjectMessage() node:%lu remote server:%lu open error\n",
LocalNodeId(), Id());
// (TBD) notify app of error ??
return;
}
}
}
else
{
if (!Open(msg.object.GetSegmentSize(),
msg.object.GetFecBlockLen(),
msg.object.GetFecNumParity()))
{
DMSG(0, "NormServerNode::HandleObjectMessage() node:%lu remote server:%lu open error\n",
LocalNodeId(), Id());
// (TBD) notify app of error ??
return;
}
}
NormObjectId objectId = msg.object.GetObjectId();
ObjectStatus objectStatus = GetObjectStatus(objectId);
if (synchronized)
{
if (OBJ_INVALID == objectStatus)
{
// (TBD) We may want to control re-sync policy options
// or at least revert to fresh sync if sync is totally lost.
DMSG(0, "NormServerNode::HandleObjectMessage() re-syncing ...\n");
Sync(objectId);
objectStatus = OBJ_NEW;
}
}
else
{
// Does this object message meet our sync policy?
if (SyncTest(msg))
{
Sync(objectId);
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() waiting to sync ...\n");
return;
}
}
NormObject* obj = NULL;
switch (objectStatus)
{
case OBJ_NEW:
SetPending(objectId);
break;
case OBJ_PENDING:
obj = rx_table.Find(objectId);
break;
case OBJ_COMPLETE:
return;
}
if (!obj)
{
if (msg.object.FlagIsSet(NormObjectMsg::FLAG_STREAM))
{
if (!(obj = new NormStreamObject(session, this, objectId)))
{
DMSG(0, "NormServerNode::HandleObjectMessage() new NORM_OBJECT_STREAM error\n");
return;
}
}
else if (msg.object.FlagIsSet(NormObjectMsg::FLAG_FILE))
{
DMSG(0, "NormServerNode::HandleObjectMessage() NORM_OBJECT_FILE not yet supported!\n");
return;
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() NORM_OBJECT_DATA not yet supported!\n");
return;
}
// Open receive object and notify app for accept.
NormObjectSize objectSize = msg.object.GetObjectSize();
if (!obj->Open(objectSize, msg.object.FlagIsSet(NormObjectMsg::FLAG_INFO)))
{
DMSG(0, "NormServerNode::HandleObjectMessage() node:%lu server:%lu "
"obj:%hu was not opened.\n", LocalNodeId(), Id(), (UINT16)objectId);
delete obj;
rx_pending_mask.Unset(objectId);
return;
}
session->Notify(NormController::RX_OBJECT_NEW, this, obj);
if (!obj->Accepted())
{
delete obj;
rx_pending_mask.Unset(objectId);
return;
}
rx_table.Insert(obj);
DMSG(0, "NormServerNode::HandleObjectMessage() node:%lu server:%lu new obj:%hu\n",
LocalNodeId(), Id(), (UINT16)objectId);
}
obj->HandleObjectMessage(msg);
} // end NormServerNode::HandleObjectMessage()
void NormServerNode::SetPending(NormObjectId objectId)
{
ASSERT(synchronized);
ASSERT(OBJ_NEW == GetObjectStatus(objectId));
if (objectId < next_id)
{
rx_pending_mask.Set(objectId);
}
else
{
rx_pending_mask.SetBits(next_id, next_id - objectId + 1);
next_id = objectId + 1;
}
} // end NormServerNode::SetPending()
void NormServerNode::Sync(NormObjectId objectId)
{
if (synchronized)
{
// Dump pending objects < objectId
if (rx_pending_mask.IsSet() && (objectId > sync_id))
{
NormObjectTable::Iterator iterator(rx_table);
NormObject* obj = iterator.GetNextObject();
while (obj && (obj->Id() < objectId))
{
DeleteObject(obj);
obj = iterator.GetNextObject();
}
rx_pending_mask.UnsetBits(sync_id, (objectId - sync_id));
}
sync_id = objectId;
if (objectId > next_id) next_id = objectId;
}
else
{
ASSERT(!rx_pending_mask.IsSet());
sync_id = next_id = objectId;
synchronized = true;
}
} // end NormServerNode::Sync()
bool NormServerNode::SyncTest(const NormMessage& msg) const
{
// (TBD) Additional sync policies
// Sync if non-repair and (INFO or block zero message)
bool result = !msg.object.FlagIsSet(NormObjectMsg::FLAG_REPAIR) &&
(NORM_MSG_INFO == msg.generic.GetType()) ?
true : (0 == msg.data.GetFecBlockId());
return result;
} // end NormServerNode::SyncTest()
void NormServerNode::DeleteObject(NormObject* obj)
{
rx_table.Remove(obj);
delete obj;
} // end NormServerNode::DeleteObject()
NormBlock* NormServerNode::GetFreeBlock(NormObjectId objectId, NormBlockId blockId)
{
NormBlock* b = block_pool.Get();
if (!b)
{
// reverse iteration to find newest object with resources
NormObjectTable::Iterator iterator(rx_table);
NormObject* obj;
while ((obj = iterator.GetPrevObject()))
{
if (obj->Id() < objectId)
{
break;
}
else
{
if (obj->Id() > objectId)
b = obj->StealNewestBlock(false);
else
b = obj->StealNewestBlock(true, blockId);
if (b)
{
b->EmptyToPool(segment_pool);
break;
}
}
}
}
return b;
} // end NormServerNode::GetFreeBlock()
char* NormServerNode::GetFreeSegment(NormObjectId objectId, NormBlockId blockId)
{
char* ptr = segment_pool.Get();
while (!ptr)
{
NormBlock* b = GetFreeBlock(objectId, blockId);
if (b)
ptr = segment_pool.Get();
else
break;
}
return ptr;
} // end NormServerNode::GetFreeSegment()
NormServerNode::ObjectStatus NormServerNode::GetObjectStatus(NormObjectId objectId) const
{
if (synchronized)
{
if (objectId < sync_id)
{
return OBJ_INVALID;
}
else
{
if (objectId < next_id)
{
if (rx_pending_mask.Test(objectId))
return OBJ_PENDING;
else
return OBJ_COMPLETE;
}
else
{
if (rx_pending_mask.IsSet())
{
if (rx_pending_mask.CanSet(objectId))
return OBJ_NEW;
else
return OBJ_INVALID;
}
else
{
NormObjectId delta = objectId - next_id + 1;
if (delta > NormObjectId(rx_pending_mask.Size()))
return OBJ_INVALID;
else
return OBJ_NEW;
}
}
}
}
else
{
return OBJ_NEW;
}
} // end NormServerNode::ObjectStatus()
// (TBD) mod repair check to do full server flush?
void NormServerNode::RepairCheck(NormObject::CheckLevel checkLevel,
NormObjectId objectId,
NormBlockId blockId,
NormSegmentId segmentId)
{
ASSERT(synchronized);
if (!repair_timer.IsActive())
{
bool startTimer = false;
if (rx_pending_mask.IsSet())
{
if (rx_repair_mask.IsSet()) rx_repair_mask.Clear();
NormObjectId nextId = rx_pending_mask.FirstSet();
NormObjectId lastId = rx_pending_mask.LastSet();
if (objectId < lastId) lastId = objectId;
while (nextId <= lastId)
{
NormObject* obj = rx_table.Find(nextId);
if (obj)
{
NormObject::CheckLevel level =
(nextId == lastId) ? checkLevel : NormObject::THRU_OBJECT;
startTimer |=
obj->ClientRepairCheck(level, blockId, segmentId, false);
}
else
{
startTimer = true;
}
nextId++;
nextId = rx_pending_mask.NextSet(nextId);
}
current_object_id = objectId;
if (startTimer)
{
DMSG(0, "NormServerNode::RepairCheck() starting NACK back-off ...\n");
}
}
else
{
// No repairs needed
return;
}
}
else if (repair_timer.RepeatCount())
{
// Repair timer in back-off phase
// Trim server current transmit position reference
if (objectId < current_object_id)
current_object_id = objectId;
}
} // end NormServerNode::RepairCheck()
// When repair timer fires, possibly build a NACK
// and queue for transmission to this server node
bool NormServerNode::OnRepairTimeout()
{
DMSG(0, "NormServerNode::OnRepairTimeout() ...\n");
switch(repair_timer.RepeatCount())
{
case 0: // hold-off time complete
break;
case 1: // back-off timeout complete
{
// 1) Were we suppressed?
if (rx_pending_mask.IsSet())
{
bool repair_pending = false;
NormObjectId nextId = rx_pending_mask.FirstSet();
NormObjectId lastId = rx_pending_mask.LastSet();
if (current_object_id < lastId) lastId = current_object_id;
while (nextId <= lastId)
{
if (!rx_repair_mask.Test(nextId))
{
NormObject* obj = rx_table.Find(nextId);
if (!obj || obj->IsRepairPending(nextId != current_object_id))
{
repair_pending = true;
break;
}
}
nextId++;
nextId = rx_pending_mask.NextSet(nextId);
} // end while (nextId <= current_block_id)
if (repair_pending)
{
// Build NACK
NormMessage msg;
NormNackMsg& nack = msg.nack;
nack.ResetNackContent();
NormRepairRequest req;
NormObjectId prevId;
UINT16 reqCount = 0;
NormRepairRequest::Form prevForm = NormRepairRequest::INVALID;
nextId = rx_pending_mask.FirstSet();
lastId = rx_pending_mask.LastSet();
if (current_object_id < lastId) lastId = current_object_id;
lastId++; // force loop to fully flush nack building.
while (nextId <= lastId)
{
NormObject* obj = NULL;
if (nextId == lastId)
nextId++; // force break of possible ending consecutive series
else
obj = rx_table.Find(nextId);
if (obj)
{
if (obj->IsPending(nextId != current_object_id))
{
if (NormRepairRequest::INVALID != prevForm)
{
nack.PackRepairRequest(req);
prevForm = NormRepairRequest::INVALID;
}
obj->AppendRepairRequest(nack); // (TBD) error check
reqCount = 0;
}
}
else
{
if (reqCount && (reqCount == (nextId - prevId)))
{
// Consecutive series of missing objects continues
reqCount++;
}
else
{
NormRepairRequest::Form nextForm;
switch (reqCount)
{
case 0:
nextForm = NormRepairRequest::INVALID;
break;
case 1:
case 2:
nextForm = NormRepairRequest::ITEMS;
break;
default:
nextForm = NormRepairRequest::RANGES;
break;
}
if (prevForm != nextForm)
{
if (NormRepairRequest::INVALID != prevForm)
nack.PackRepairRequest(req); // (TBD) error check
if (NormRepairRequest::INVALID != nextForm)
{
nack.AttachRepairRequest(req, segment_size); // (TBD) error check
req.SetForm(nextForm);
req.SetFlag(NormRepairRequest::OBJECT);
}
prevForm = nextForm;
}
switch (nextForm)
{
case NormRepairRequest::ITEMS:
req.AppendRepairItem(prevId, 0, 0);
if (2 == reqCount)
req.AppendRepairItem(prevId+1, 0, 0);
break;
case NormRepairRequest::RANGES:
req.AppendRepairItem(prevId, 0, 0);
req.AppendRepairItem(prevId+reqCount-1, 0, 0);
default:
break;
}
prevId = nextId;
reqCount = 1;
}
}
nextId++;
if (nextId <= lastId)
nextId = rx_pending_mask.NextSet(nextId);
} // end while (nextId <= lastId)
// (TBD) Queue NACK for transmission
DMSG(0, "NormServerNode::OnRepairTimeout() NACK TRANSMITTED ...\n");
}
else
{
DMSG(0, "NormServerNode::OnRepairTimeout() NACK SUPPRESSED ...\n");
// (TBD) repair_timer.SetInterval(HOLD_OFF_INTERVAL)
}
}
else
{
DMSG(0, "NormServerNode::OnRepairTimeout() nothing pending ...\n");
// (TBD) cancel hold-off timeout ???
} // end if/else (repair_pending)
}
break;
default: // should never occur
ASSERT(0);
break;
}
return true;
} // end NormServerNode::OnRepairTimeout()
NormNodeTree::NormNodeTree()
: root(NULL)
{
}
NormNodeTree::~NormNodeTree()
{
Destroy();
}
NormNode *NormNodeTree::FindNodeById(unsigned long nodeId) const
{
NormNode* x = root;
while(x && (x->id != nodeId))
{
if (nodeId < x->id)
x = x->left;
else
x = x->right;
}
return x;
} // end NormNodeTree::FindNodeById()
void NormNodeTree::AttachNode(NormNode *node)
{
ASSERT(node);
node->left = NULL;
node->right = NULL;
NormNode *x = root;
while (x)
{
if (node->id < x->id)
{
if (!x->left)
{
x->left = node;
node->parent = x;
return;
}
else
{
x = x->left;
}
}
else
{
if (!x->right)
{
x->right = node;
node->parent = x;
return;
}
else
{
x = x->right;
}
}
}
root = node; // root _was_ NULL
} // end NormNodeTree::AddNode()
void NormNodeTree::DetachNode(NormNode* node)
{
ASSERT(node);
NormNode* x;
NormNode* y;
if (!node->left || !node->right)
{
y = node;
}
else
{
if (node->right)
{
y = node->right;
while (y->left) y = y->left;
}
else
{
x = node;
y = node->parent;
while(y && (y->right == x))
{
x = y;
y = y->parent;
}
}
}
if (y->left)
x = y->left;
else
x = y->right;
if (x) x->parent = y->parent;
if (!y->parent)
root = x;
else if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
if (node != y)
{
if ((y->parent = node->parent))
{
if (y->id < y->parent->id)
y->parent->left = y;
else
y->parent->right = y;
}
else
{
root = y;
}
if ((y->left = node->left)) y->left->parent = y;
if ((y->right = node->right)) y->right->parent = y;
}
} // end NormNodeTree::DetachNode()
void NormNodeTree::Destroy()
{
NormNode* n;
while ((n = root))
{
DetachNode(n);
delete n;
}
} // end NormNodeTree::Destroy()
NormNodeTreeIterator::NormNodeTreeIterator(const NormNodeTree& t)
: tree(t)
{
NormNode* x = t.root;
if (x)
{
while (x->left) x = x->left;
next = x;
}
}
void NormNodeTreeIterator::Reset()
{
NormNode* x = tree.root;
if (x)
{
while (x->left) x = x->left;
next = x;
}
} // end NormNodeTreeIterator::Reset()
NormNode* NormNodeTreeIterator::GetNextNode()
{
NormNode* n = next;
if (n)
{
if (next->right)
{
NormNode* y = n->right;
while (y->left) y = y->left;
next = y;
}
else
{
NormNode* x = n;
NormNode* y = n->parent;
while(y && (y->right == x))
{
x = y;
y = y->parent;
}
next = y;
}
}
return n;
} // end NormNodeTreeIterator::GetNextNode()
NormNodeList::NormNodeList()
: head(NULL), tail(NULL)
{
}
NormNodeList::~NormNodeList()
{
Destroy();
}
NormNode* NormNodeList::FindNodeById(NormNodeId nodeId) const
{
NormNode *next = head;
while (next)
{
if (nodeId == next->id)
return next;
else
next = next->right;
}
return NULL;
} // NormNodeList::Find()
void NormNodeList::Append(NormNode *theNode)
{
ASSERT(theNode);
theNode->left = tail;
if (tail)
tail->right = theNode;
else
head = theNode;
tail = theNode;
theNode->right = NULL;
} // end NormNodeList::Append()
void NormNodeList::Remove(NormNode *theNode)
{
ASSERT(theNode);
if (theNode->right)
theNode->right->left = theNode->left;
else
tail = theNode->left;
if (theNode->left)
theNode->left->right = theNode->right;
else
head = theNode->right;
} // end NormNodeList::Remove()
void NormNodeList::Destroy()
{
NormNode* n;
while ((n = head))
{
Remove(n);
delete n;
}
} // end NormNodeList::Destroy()

206
common/normNode.h Normal file
View File

@ -0,0 +1,206 @@
#ifndef _NORM_NODE
#define _NORM_NODE
#include "normMessage.h"
#include "normObject.h"
#include "normEncoder.h"
#include "protocolTimer.h"
class NormNode
{
friend class NormNodeTree;
friend class NormNodeTreeIterator;
friend class NormNodeList;
friend class NormNodeListIterator;
public:
NormNode(class NormSession* theSession, NormNodeId nodeId);
virtual ~NormNode();
void SetAddress(const NetworkAddress& address)
{addr = address;}
const NetworkAddress Address() const {return addr;}
const NormNodeId& Id() {return id;}
inline const NormNodeId& LocalNodeId();
protected:
class NormSession* session;
private:
NormNodeId id;
NetworkAddress addr;
NormNode* parent;
NormNode* right;
NormNode* left;
NormNode* prev;
NormNode* next;
}; // end class NormNode
class NormServerNode : public NormNode
{
public:
enum ObjectStatus {OBJ_INVALID, OBJ_NEW, OBJ_PENDING, OBJ_COMPLETE};
NormServerNode(class NormSession* theSession, NormNodeId nodeId);
~NormServerNode();
void HandleObjectMessage(NormMessage& msg);
bool Open(UINT16 segmentSize, UINT16 numData, UINT16 numParity);
void Close();
bool IsOpen() const {return is_open;}
bool SyncTest(const NormMessage& msg) const;
void Sync(NormObjectId objectId);
ObjectStatus GetObjectStatus(NormObjectId objectId) const;
void SetPending(NormObjectId objectId);
void DeleteObject(NormObject* obj);
UINT16 SegmentSize() {return segment_size;}
UINT16 BlockSize() {return ndata;}
UINT16 NumParity() {return nparity;}
//NormBlockPool* BlockPool() {return &block_pool;}
//NormSegmentPool* SegmentPool() {return &segment_pool;}
NormBlock* GetFreeBlock(NormObjectId objectId, NormBlockId blockId);
void PutFreeBlock(NormBlock* block)
{
block->EmptyToPool(segment_pool);
block_pool.Put(block);
}
char* GetFreeSegment(NormObjectId objectId, NormBlockId blockId);
void PutFreeSegment(char* segment)
{segment_pool.Put(segment);}
void SetErasureLoc(UINT16 index, UINT16 value)
{
ASSERT(index < (nparity));
erasure_loc[index] = value;
}
UINT16 GetErasureLoc(UINT16 index)
{return erasure_loc[index];}
UINT16 Decode(char** segmentList, UINT16 numData, UINT16 erasureCount)
{
return decoder.Decode(segmentList, numData, erasureCount, erasure_loc);
}
private:
void RepairCheck(NormObject::CheckLevel checkLevel,
NormObjectId objectId,
NormBlockId blockId,
NormSegmentId segmentId);
bool OnRepairTimeout();
bool synchronized;
NormObjectId sync_id; // only valid if(synchronized)
NormObjectId next_id; // only valid if(synchronized)
bool is_open;
UINT16 segment_size;
UINT16 ndata;
UINT16 nparity;
NormObjectTable rx_table;
NormSlidingMask rx_pending_mask;
NormSlidingMask rx_repair_mask;
NormBlockPool block_pool;
NormSegmentPool segment_pool;
NormDecoder decoder;
UINT16* erasure_loc;
ProtocolTimer repair_timer;
NormObjectId current_object_id; // index for repair
}; // end class NodeServerNode
// Used for binary trees of NormNodes
class NormNodeTree
{
friend class NormNodeTreeIterator;
public:
// Methods
NormNodeTree();
~NormNodeTree();
NormNode* FindNodeById(NormNodeId nodeId) const;
void DeleteNode(NormNode *theNode)
{
ASSERT(theNode);
DetachNode(theNode);
delete theNode;
}
void AttachNode(NormNode *theNode);
void DetachNode(NormNode *theNode);
void Destroy(); // delete all nodes in tree
private:
// Members
NormNode* root;
}; // end class NormNodeTree
class NormNodeTreeIterator
{
public:
NormNodeTreeIterator(const NormNodeTree& nodeTree);
void Reset();
NormNode* GetNextNode();
private:
const NormNodeTree& tree;
NormNode* next;
}; // end class NormNodeTreeIterator
class NormNodeList
{
friend class NormNodeListIterator;
public:
// Construction
NormNodeList();
~NormNodeList();
NormNode* FindNodeById(NormNodeId nodeId) const;
void Append(NormNode* theNode);
void Remove(NormNode* theNode);
void DeleteNode(NormNode* theNode)
{
ASSERT(theNode);
Remove(theNode);
delete theNode;
}
void Destroy(); // delete all nodes in list
// Members
private:
NormNode* head;
NormNode* tail;
}; // end class NormNodeList
class NormNodeListIterator
{
public:
NormNodeListIterator(const NormNodeList& nodeList)
: list(nodeList), next(nodeList.head) {}
void Reset() {next = list.head;}
NormNode* GetNextNode()
{
NormNode* n = next;
next = n ? n->next : NULL;
return n;
}
private:
const NormNodeList& list;
NormNode* next;
}; // end class NormNodeListIterator
#endif // NORM_NODE

1349
common/normObject.cpp Normal file

File diff suppressed because it is too large Load Diff

218
common/normObject.h Normal file
View File

@ -0,0 +1,218 @@
#ifndef _NORM_OBJECT
#define _NORM_OBJECT
#include "normSegment.h" // NORM segmentation classes
#include <stdio.h>
class NormObject
{
friend class NormObjectTable;
public:
enum Type
{
DATA,
FILE,
STREAM
};
enum CheckLevel
{
THRU_INFO,
TO_BLOCK,
THRU_SEGMENT,
THRU_BLOCK,
THRU_OBJECT
};
virtual ~NormObject();
NormObject::Type GetType() const {return type;}
const NormObjectId& Id() const {return id;}
const NormObjectSize& Size() {return object_size;}
bool IsStream() {return (STREAM == type);}
NormNodeId LocalNodeId() const;
// Opens (inits) object for tx operation
bool Open(const NormObjectSize& objectSize,
const char* infoPtr,
UINT16 infoLen);
// Opens (inits) object for rx operation
bool Open(const NormObjectSize& objectSize, bool hasInfo)
{return Open(objectSize, NULL, hasInfo ? 1 : 0);}
void Close();
virtual bool WriteSegment(NormBlockId blockId,
NormSegmentId segmentId,
const char* buffer) = 0;
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId,
NormObjectSize* offset, char* buffer, UINT16 maxlen) = 0;
// These are only valid after object is open
NormBlockId LastBlockId();
NormSegmentId LastSegmentId();
NormSegmentId LastSegmentId(NormBlockId blockId);
// (TBD) re-write to use current_block_id/next_segment_id when
// flush == false !!!
bool IsPending(bool flush = true)
{return (pending_info || pending_mask.IsSet());}
// Methods available to server for transmission
bool NextServerMsg(NormMessage* msg);
// Used by session server for resource management scheme
NormBlock* StealOldestBlock(bool excludeBlock, NormBlockId excludeId = 0);
// Methods available to client for reception
bool Accepted() {return accepted;}
void HandleObjectMessage(NormMessage& msg);
bool StreamUpdateStatus(NormBlockId blockId);
// Used by remote server node for resource management scheme
NormBlock* StealNewestBlock(bool excludeBlock, NormBlockId excludeId = 0);
bool ClientRepairCheck(CheckLevel level,
NormBlockId blockId,
NormSegmentId segmentId,
bool timerActive);
bool IsRepairPending(bool flush);
bool AppendRepairRequest(NormNackMsg& nack) {return true;}
protected:
NormObject(NormObject::Type theType,
class NormSession* theSession,
class NormServerNode* theServer,
const NormObjectId& objectId);
void Accept() {accepted = true;}
NormObject::Type type;
class NormSession* session;
class NormServerNode* server; // NULL value indicates local (tx) object
NormObjectId id;
NormObjectSize object_size;
UINT16 segment_size;
UINT16 ndata;
UINT16 nparity;
NormBlockBuffer block_buffer;
bool pending_info;
NormSlidingMask pending_mask;
bool repair_info;
NormSlidingMask repair_mask;
NormBlockId current_block_id;
NormSegmentId next_segment_id;
NormBlockId last_block_id;
NormSegmentId last_segment_id;
char* info;
UINT16 info_len;
bool accepted;
// Extra state for STREAM objects
bool stream_sync;
NormBlockId stream_sync_id;
NormBlockId stream_next_id;
NormObject* next;
}; // end class NormObject
class NormStreamObject : public NormObject
{
public:
NormStreamObject(class NormSession* theSession,
class NormServerNode* theServer,
const NormObjectId& objectId);
~NormStreamObject();
bool Open(unsigned long bufferSize,
const char* infoPtr = NULL,
UINT16 infoLen = 0);
void Close();
bool Accept(unsigned long bufferSize);
virtual bool WriteSegment(NormBlockId blockId,
NormSegmentId segmentId,
const char* buffer);
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId,
NormObjectSize* offset, char* buffer, UINT16 maxlen);
unsigned long Read(char* buffer, unsigned long len);
unsigned long Write(char* buffer, unsigned long len, bool flush = false);
// For receive stream, we can rewind to earliest buffered offset
void Rewind();
private:
class Index
{
public:
NormBlockId block;
NormSegmentId segment;
};
NormBlockPool block_pool;
NormSegmentPool segment_pool;
NormBlockBuffer stream_buffer;
Index write_index;
NormObjectSize write_offset;
Index read_index;
NormObjectSize read_offset;
}; // end class NormStreamObject
class NormObjectTable
{
friend class NormObjectTable::Iterator;
public:
NormObjectTable();
~NormObjectTable();
bool Init(UINT16 rangeMax, UINT16 tableSize = 256);
void Destroy();
bool IsInited() {return (NULL != table);}
bool Insert(NormObject* theObject);
bool Remove(const NormObject* theObject);
NormObject* Find(const NormObjectId& objectId) const;
NormObjectId RangeLo() {return range_lo;}
NormObjectId RangeHi() {return range_hi;}
bool IsEmpty() {return (0 == range);}
class Iterator
{
public:
Iterator(const NormObjectTable& objectTable);
NormObject* GetNextObject();
NormObject* GetPrevObject();
void Reset() {reset = true;}
private:
const NormObjectTable& table;
bool reset;
NormObjectId index;
bool backwards;
};
private:
NormObject* Next(NormObject* o) const {return o->next;}
NormObject** table;
unsigned long hash_mask;
unsigned long range_max; // max range of objects that can be kept
unsigned long range; // zero if "object table" is empty
NormObjectId range_lo;
NormObjectId range_hi;
}; // end class NormObjectTable
#endif // _NORM_OBJECT

551
common/normSegment.cpp Normal file
View File

@ -0,0 +1,551 @@
#include "normSegment.h"
#include <errno.h> // for strerror()
NormSegmentPool::NormSegmentPool()
: seg_size(0), seg_count(0), seg_total(0), seg_list(NULL),
peak_usage(0), overruns(0), overrun_flag(false)
{
}
NormSegmentPool::~NormSegmentPool()
{
Destroy();
}
bool NormSegmentPool::Init(unsigned int count, unsigned int size)
{
if (seg_list) Destroy();
peak_usage = 0;
overruns = 0;
// This makes sure we get appropriate alignment
unsigned int alloc_size = size / sizeof(char*);
if ((alloc_size*sizeof(char*)) < size) alloc_size++;
seg_size = alloc_size * sizeof(char*);
for (unsigned int i = 0; i < count; i++)
{
char** ptr = new char*[alloc_size];
if (ptr)
{
*ptr = seg_list;
seg_list = (char*)ptr;
seg_count++;
}
else
{
DMSG(0, "NormSegmentPool::Init() memory allocation error: %s\n",
strerror(errno));
seg_total = seg_count;
Destroy();
return false;
}
}
seg_total = seg_count;
return true;
} // end NormSegmentPool::Init()
void NormSegmentPool::Destroy()
{
ASSERT(seg_count == seg_total);
char** ptr = (char**)seg_list;
while (ptr)
{
char* next = *ptr;
delete ptr;
ptr = (char**)next;
}
seg_list = NULL;
seg_count = 0;
seg_total = 0;
seg_size = 0;
} // end NormSegmentPool::Destroy()
char* NormSegmentPool::Get()
{
char** ptr = (char**)seg_list;
if (ptr)
{
seg_list = *ptr;
seg_count--;
#ifdef NORM_DEBUG
overrun_flag = false;
unsigned int usage = seg_total - seg_count;
if (usage > peak_usage) peak_usage = usage;
}
else
{
if (!overrun_flag)
{
overruns++;
overrun_flag = true;
}
#endif // NORM_DEBUG
}
return ((char*)ptr);
} // end NormSegmentPool::GetSegment()
////////////////////////////////////////////////////////////
// NormBlock Implementation
NormBlock::NormBlock()
: size(0), segment_table(NULL), erasure_count(0), parity_count(0)
{
}
NormBlock::~NormBlock()
{
Destroy();
}
bool NormBlock::Init(UINT16 blockSize)
{
if (segment_table) Destroy();
if (!(segment_table = new char*[blockSize]))
{
DMSG(0, "NormBlock::Init() segment_table allocation error: %s\n", strerror(errno));
return false;
}
memset(segment_table, 0, blockSize*sizeof(char*));
if (!pending_mask.Init(blockSize))
{
DMSG(0, "NormBlock::Init() pending_mask allocation error: %s\n", strerror(errno));
Destroy();
return false;
}
if (!repair_mask.Init(blockSize))
{
DMSG(0, "NormBlock::Init() repair_mask allocation error: %s\n", strerror(errno));
Destroy();
return false;
}
size = blockSize;
erasure_count = 0;
parity_count = 0;
return true;
} // end NormBlock::Init()
void NormBlock::Destroy()
{
repair_mask.Destroy();
pending_mask.Destroy();
// (TBD) Option to return segments to pool from which they came
if (segment_table)
{
for (unsigned int i = 0; i < size; i++)
if (segment_table[i]) delete []segment_table[i];
delete []segment_table;
segment_table = (char**)NULL;
}
erasure_count = parity_count = size = 0;
} // end NormBlock::Destroy()
void NormBlock::EmptyToPool(NormSegmentPool& segmentPool)
{
ASSERT(segment_table);
for (unsigned int i = 0; i < size; i++)
{
if (segment_table[i])
{
segmentPool.Put(segment_table[i]);
segment_table[i] = (char*)NULL;
}
}
} // end NormBlock::EmptyToPool()
bool NormBlock::IsEmpty()
{
ASSERT(segment_table);
for (unsigned int i = 0; i < size; i++)
if (segment_table[i]) return false;
return true;
} // end NormBlock::EmptyToPool()
bool NormBlock::IsRepairPending(NormSegmentId end)
{
ASSERT(end <= repair_mask.Size());
repair_mask.SetBits(end, repair_mask.Size() - end);
repair_mask.XCopy(pending_mask);
return (repair_mask.IsSet() ? (repair_mask.FirstSet() < end) : false);
} // end NormBlock::IsRepairPending()
NormBlockPool::NormBlockPool()
: head((NormBlock*)NULL)
{
}
NormBlockPool::~NormBlockPool()
{
Destroy();
}
bool NormBlockPool::Init(UINT32 numBlocks, UINT16 blockSize)
{
if (head) Destroy();
for (UINT32 i = 0; i < numBlocks; i++)
{
NormBlock* b = new NormBlock();
if (b)
{
if (!b->Init(blockSize))
{
DMSG(0, "NormBlockPool::Init() block init error\n");
delete b;
Destroy();
return false;
}
b->next = head;
head = b;
}
else
{
DMSG(0, "NormBlockPool::Init() new block error\n");
Destroy();
return false;
}
}
return true;
} // end NormBlockPool::Init()
void NormBlockPool::Destroy()
{
NormBlock* next;
while ((next = head))
{
head = next->next;
delete next;
}
} // end NormBlockPool::Destroy()
NormBlockBuffer::NormBlockBuffer()
: table((NormBlock**)NULL), range_max(0), range(0)
{
}
NormBlockBuffer::~NormBlockBuffer()
{
Destroy();
}
bool NormBlockBuffer::Init(unsigned long rangeMax, unsigned long tableSize)
{
if (table) Destroy();
// Make sure tableSize is greater than 0 and 2^n
if (!rangeMax || !tableSize)
{
DMSG(0, "NormBlockBuffer::Init() bad range(%lu) or tableSize(%lu)\n",
rangeMax, tableSize);
return false;
}
if (0 != (tableSize & 0x07)) tableSize = (tableSize >> 3) + 1;
if (!(table = new NormBlock*[tableSize]))
{
DMSG(0, "NormBlockBuffer::Init() buffer allocation error: %s\n", strerror(errno));
return false;
}
memset(table, 0, tableSize*sizeof(char*));
hash_mask = tableSize - 1;
range_max = rangeMax;
range = 0;
return true;
} // end NormBlockBuffer::Init()
void NormBlockBuffer::Destroy()
{
range_max = range = 0;
if (table)
{
NormBlock* block;
while((block = Find(range_lo)))
{
DMSG(0, "NormBlockBuffer::Destroy() buffer not empty!?\n");
Remove(block);
delete block;
}
delete []table;
table = (NormBlock**)NULL;
range_max = 0;
}
} // end NormBlockBuffer::Destroy()
NormBlock* NormBlockBuffer::Find(const NormBlockId& blockId) const
{
if (range)
{
if ((blockId < range_lo) || (blockId > range_hi)) return (NormBlock*)NULL;
NormBlock* theBlock = table[((UINT32)blockId) & hash_mask];
//printf("NormBlockBuffer::Find() table[%lu] = %p\n", ((UINT32)blockId) & hash_mask, theBlock);
while (theBlock && (blockId != theBlock->Id())) theBlock = theBlock->next;
return theBlock;
}
else
{
return (NormBlock*)NULL;
}
} // end NormBlockBuffer::Find()
bool NormBlockBuffer::CanInsert(NormBlockId blockId) const
{
if (0 != range)
{
if (blockId < range_lo)
{
if ((range_lo - blockId + range) > range_max)
return false;
else
return true;
}
else if (blockId > range_hi)
{
if ((blockId - range_hi + range) > range_max)
return false;
else
return true;
}
else
{
return true;
}
}
else
{
return true;
}
} // end NormBlockBuffer::CanInsert()
bool NormBlockBuffer::Insert(NormBlock* theBlock)
{
if (range < range_max)
{
const NormBlockId& blockId = theBlock->Id();
if (!range)
{
range_lo = range_hi = blockId;
range = 1;
}
if (blockId < range_lo)
{
UINT32 newRange = range_lo - blockId + range;
if (newRange > range_max) return false;
range_lo = blockId;
range = newRange;
}
else if (blockId > range_hi)
{
UINT32 newRange = blockId - range_hi + range;
if (newRange > range_max) return false;
range_hi = blockId;
range = newRange;
}
UINT32 index = ((UINT32)blockId) & hash_mask;
NormBlock* prev = NULL;
NormBlock* entry = table[index];
while (entry && (entry->Id() < blockId))
{
prev = entry;
entry = entry->next;
}
if (prev)
prev->next = theBlock;
else
table[index] = theBlock;
ASSERT((entry ? (blockId != entry->Id()) : true));
theBlock->next = entry;
return true;
}
else
{
return false;
}
} // end NormBlockBuffer::Insert()
bool NormBlockBuffer::Remove(const NormBlock* theBlock)
{
ASSERT(theBlock);
const NormBlockId& blockId = theBlock->Id();
if (range)
{
if ((blockId < range_lo) || (blockId > range_hi)) return false;
UINT32 index = ((UINT32)blockId) & hash_mask;
NormBlock* prev = NULL;
NormBlock* entry = table[index];
while (entry && (entry->Id() != blockId))
{
prev = entry;
entry = entry->next;
}
if (entry != theBlock) return false;
if (prev)
prev->next = entry->next;
else
table[index] = (NormBlock*)NULL;
if (range > 1)
{
if (blockId == range_lo)
{
// Find next entry for range_lo
UINT32 i = index;
UINT32 endex;
if (range <= hash_mask)
endex = (index + range) & hash_mask;
else
endex = index;
entry = NULL;
UINT32 offset = 0;
NormBlockId nextId = range_hi;
do
{
++i &= hash_mask;
offset++;
if ((entry = table[i]))
{
NormBlockId id = (UINT32)index + offset;
while(entry && (entry->Id() != id))
{
if ((entry->Id() > blockId) && (entry->Id() < nextId))
nextId = entry->Id();
entry = entry->next;
}
if (entry) break;
}
} while (i != endex);
if (entry)
{
range_lo = entry->Id();
range = range_hi - range_lo + 1;
}
else if (nextId != range_hi)
{
range_lo = nextId;
range = range_hi - range_lo + 1;
}
else
{
range = 0;
}
}
else if (blockId == range_hi)
{
// Find prev entry for range_hi
UINT32 i = index;
UINT32 endex;
if (range <= hash_mask)
endex = (index - range) & hash_mask;
else
endex = index;
entry = NULL;
UINT32 offset = 0;
//printf("preving i:%lu endex:%lu lo:%lu hi:%lu\n", i, endex, (UINT32)range_lo, (UINT32) range_hi);
NormBlockId prevId = range_lo;
do
{
--i &= hash_mask;
offset++;
if ((entry = table[i]))
{
NormBlockId id = (UINT32)index - offset;
//printf("Looking for id:%lu at index:%lu\n", (UINT32)id, i);
while(entry && (entry->Id() != id))
{
if ((entry->Id() < blockId) && (entry->Id() > prevId))
prevId = entry->Id();
entry = entry->next;
}
if (entry) break;
}
} while (i != endex);
if (entry)
{
range_hi = entry->Id();
}
else if (prevId != range_lo)
{
range_hi = prevId;
range = range_hi - range_lo + 1;
}
else
{
range = 0;
}
}
}
else
{
range = 0;
}
return true;
}
else
{
return false;
}
} // end NormBlockBuffer::Remove()
NormBlockBuffer::Iterator::Iterator(const NormBlockBuffer& blockBuffer)
: buffer(blockBuffer), reset(true)
{
}
NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
{
if (reset)
{
if (buffer.range)
{
reset = false;
index = buffer.range_lo;
return buffer.Find(index);
}
else
{
return (NormBlock*)NULL;
}
}
else
{
if (buffer.range &&
(index < buffer.range_hi) &&
!(index < buffer.range_lo))
{
// Find next entry _after_ current "index"
UINT32 i = index;
UINT32 endex = buffer.range_hi - index;
if (endex <= buffer.hash_mask)
endex = (index + endex) & buffer.hash_mask;
else
endex = index;
UINT32 offset = 0;
NormBlockId nextId = buffer.range_hi;
do
{
++i &= buffer.hash_mask;
offset++;
NormBlockId id = (UINT32)index + offset;
NormBlock* entry = buffer.table[i];
while ((NULL != entry )& (entry->Id() != id))
{
if ((entry->Id() > index) && (entry->Id() < nextId))
nextId = entry->Id();
entry = NormBlockBuffer::Next(entry);
}
if (entry)
{
index = entry->Id();
return entry;
}
} while (i != endex);
// If we get here, use nextId value
index = nextId;
return buffer.Find(nextId);
}
else
{
return (NormBlock*)NULL;
}
}
} // end NormBlockBuffer::Iterator::GetNextBlock()

224
common/normSegment.h Normal file
View File

@ -0,0 +1,224 @@
#ifndef _NORM_SEGMENT
#define _NORM_SEGMENT
#include "normMessage.h"
#include "normBitmask.h"
// Norm uses preallocated (or dynamically allocated) pools of
// segments (vectors) for different buffering purposes
class NormSegmentPool
{
public:
NormSegmentPool();
~NormSegmentPool();
bool Init(unsigned int count, unsigned int size);
void Destroy();
char* Get();
void Put(char* segment)
{
ASSERT(seg_count < seg_total);
char** ptr = (char**)segment;
*ptr = seg_list;
seg_list = segment;
seg_count++;
}
bool IsEmpty() {return (NULL == seg_list);}
private:
unsigned int seg_size;
unsigned int seg_count;
unsigned int seg_total;
char* seg_list;
unsigned int peak_usage;
unsigned int overruns;
bool overrun_flag;
}; // end class NormSegmentPool
class NormBlock
{
friend class NormBlockPool;
friend class NormBlockBuffer;
public:
enum Flag
{
PARITY_READY = 0x01,
IN_REPAIR = 0x02
};
NormBlock();
~NormBlock();
const NormBlockId& Id() const {return id;}
void SetId(NormBlockId& x) {id = x;}
bool Init(UINT16 blockSize);
void Destroy();
void SetFlag(NormBlock::Flag flag) {flags |= flag;}
bool ParityReady() {return (0 != (flags & PARITY_READY));}
bool InRepair() {return (0 != (flags & IN_REPAIR));}
char** SegmentList(UINT16 index = 0) {return &segment_table[index];}
char* Segment(NormSegmentId sid)
{
ASSERT(sid < size);
return segment_table[sid];
}
void AttachSegment(NormSegmentId sid, char* segment)
{
ASSERT(sid < size);
ASSERT(!segment_table[sid]);
segment_table[sid] = segment;
erasure_count--;
}
char* DetachSegment(NormSegmentId sid)
{
ASSERT(sid < size);
char* segment = segment_table[sid];
segment_table[sid] = (char*)NULL;
return segment;
}
void SetSegment(NormSegmentId sid, char* segment)
{
ASSERT(sid < size);
ASSERT(!segment_table[sid]);
segment_table[sid] = segment;
}
void TxInit(NormBlockId& blockId, UINT16 ndata, UINT16 autoParity)
{
id = blockId;
pending_mask.Clear();
pending_mask.SetBits(0, ndata+autoParity);
repair_mask.Clear();
erasure_count = size - ndata;
parity_count = 0;
flags = 0;
}
void RxInit(NormBlockId& blockId, UINT16 ndata)
{
id = blockId;
pending_mask.Reset();
repair_mask.Clear();
erasure_count = size;
parity_count = 0;
flags = 0;
}
NormSymbolId FirstPending() const
{return pending_mask.FirstSet();}
NormSymbolId FirstRepair() const
{return repair_mask.FirstSet();}
bool SetPending(NormSymbolId s)
{return pending_mask.Set(s);}
void UnsetPending(NormSymbolId s)
{pending_mask.Unset(s);}
void ClearPending()
{pending_mask.Clear();}
bool SetRepair(NormSymbolId s)
{return repair_mask.Set(s);}
void UnsetRepair(NormSymbolId s)
{repair_mask.Unset(s);}
void ClearRepairs()
{repair_mask.Clear();}
bool IsPending(NormSymbolId s) const
{return pending_mask.Test(s);}
bool IsPending() const
{return pending_mask.IsSet();}
bool IsRepairPending(NormSegmentId end);
NormSymbolId NextPending(NormSymbolId index) const
{return pending_mask.NextSet(index);}
UINT16 ErasureCount() const {return erasure_count;}
//void DisplayPendingMask(FILE* f) {pending_mask.Display(f);}
bool IsEmpty();
void EmptyToPool(NormSegmentPool& segmentPool);
private:
NormBlockId id;
UINT16 size;
char** segment_table;
int flags;
UINT16 erasure_count;
UINT16 parity_count;
NormBitmask pending_mask;
NormBitmask repair_mask;
NormBlock* next;
}; // end class NormBlock
class NormBlockPool
{
public:
NormBlockPool();
~NormBlockPool();
bool Init(UINT32 numBlocks, UINT16 blockSize);
void Destroy();
bool IsEmpty() {return (NULL == head);}
NormBlock* Get()
{
NormBlock* b = head;
head = b ? b->next : NULL;
return b;
}
void Put(NormBlock* b)
{
b->next = head;
head = b;
}
private:
NormBlock* head;
}; // end class NormBlockPool
class NormBlockBuffer
{
friend class NormBlockBuffer::Iterator;
public:
NormBlockBuffer();
~NormBlockBuffer();
bool Init(unsigned long rangeMax, unsigned long tableSize = 256);
void Destroy();
bool Insert(NormBlock* theBlock);
bool Remove(const NormBlock* theBlock);
NormBlock* Find(const NormBlockId& blockId) const;
NormBlockId RangeLo() {return range_lo;}
NormBlockId RangeHi() {return range_hi;}
bool IsEmpty() {return (0 == range);}
bool CanInsert(NormBlockId blockId) const;
class Iterator
{
public:
Iterator(const NormBlockBuffer& blockBuffer);
NormBlock* GetNextBlock();
void Reset() {reset = true;}
private:
const NormBlockBuffer& buffer;
bool reset;
NormBlockId index;
};
private:
static NormBlock* Next(NormBlock* b) {return b->next;}
NormBlock** table;
unsigned long hash_mask;
unsigned long range_max; // max range of blocks that can be buffered
unsigned long range; // zero if "block buffer" is empty
NormBlockId range_lo;
NormBlockId range_hi;
}; // end class NormBlockBuffer
#endif // _NORM_SEGMENT

547
common/normSession.cpp Normal file
View File

@ -0,0 +1,547 @@
#include "normSession.h"
#include <errno.h>
NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
: session_mgr(sessionMgr), local_node_id(localNodeId),
tx_rate(DEFAULT_TRANSMIT_RATE),
is_server(false), flush_count(NORM_ROBUST_FACTOR),
posted_tx_queue_empty(false),
is_client(false),
next(NULL)
{
tx_socket.Init((UdpSocketOwner*) this,
(UdpSocketRecvHandler)&NormSession::TxSocketRecvHandler,
sessionMgr.SocketInstaller(),
sessionMgr.SocketInstallData());
rx_socket.Init((UdpSocketOwner*) this,
(UdpSocketRecvHandler)&NormSession::RxSocketRecvHandler,
sessionMgr.SocketInstaller(),
sessionMgr.SocketInstallData());
tx_timer.Init(0.0, -1,
(ProtocolTimerOwner *)this,
(ProtocolTimeoutFunc)&NormSession::OnTxTimeout);
}
NormSession::~NormSession()
{
Close();
}
const double NormSession::DEFAULT_TRANSMIT_RATE = 8000.0;
bool NormSession::Open()
{
ASSERT(address.IsValid());
if (!tx_socket.IsOpen())
{
if (UDP_SOCKET_ERROR_NONE != tx_socket.Open())
{
DMSG(0, "NormSession::Open() tx_socket open error\n");
return false;
}
}
if (!rx_socket.IsOpen())
{
if (UDP_SOCKET_ERROR_NONE != rx_socket.Open(address.Port()))
{
DMSG(0, "NormSession::Open() rx_socket open error\n");
Close();
return false;
}
if (address.IsMulticast())
{
if (UDP_SOCKET_ERROR_NONE != rx_socket.JoinGroup(&address, ttl))
{
DMSG(0, "NormSession::Open() rx_socket join group error\n");
Close();
return false;
}
}
}
for (unsigned int i = 0; i < DEFAULT_MESSAGE_POOL_DEPTH; i++)
{
NormMessage* msg = new NormMessage();
if (msg)
{
message_pool.Append(msg);
}
else
{
DMSG(0, "NormSession::Open() new message error: %s\n", strerror(errno));
Close();
return false;
}
}
return true;
} // end NormSession::Open()
void NormSession::Close()
{
if (is_server) StopServer();
if (is_client) StopClient();
if (tx_timer.IsActive()) tx_timer.Deactivate();
message_queue.Destroy();
message_pool.Destroy();
if (tx_socket.IsOpen()) tx_socket.Close();
if (rx_socket.IsOpen()) rx_socket.Close();
} // end NormSession::Close()
bool NormSession::StartServer(unsigned long bufferSpace,
UINT16 segmentSize,
UINT16 numData,
UINT16 numParity)
{
if (!IsOpen())
{
if (!Open()) return false;
}
// (TBD) parameterize the object history depth
if (!tx_table.Init(256))
{
DMSG(0, "NormSession::StartServer() tx_table.Init() error!\n");
StopServer();
return false;
}
if (!tx_pending_mask.Init(256))
{
DMSG(0, "NormSession::StartServer() tx_pending_mask.Init() error!\n");
StopServer();
return false;
}
if (!tx_repair_mask.Init(256))
{
DMSG(0, "NormSession::StartServer() tx_repair_mask.Init() error!\n");
StopServer();
return false;
}
// Calculate how much memory each buffered block will require
UINT16 blockSize = numData + numParity;
unsigned long maskSize = blockSize >> 3;
if (0 != (blockSize & 0x07)) maskSize++;
unsigned long blockSpace = sizeof(NormBlock) +
blockSize * sizeof(char*) +
2*maskSize +
numParity * (segmentSize + NormDataMsg::PayloadHeaderLen());
unsigned long numBlocks = bufferSpace / blockSpace;
if (bufferSpace > (numBlocks*blockSpace)) numBlocks++;
if (numBlocks < 2) numBlocks = 2;
unsigned long numSegments = numBlocks * numParity;
if (!block_pool.Init(numBlocks, blockSize))
{
DMSG(0, "NormSession::StartServer() block_pool init error\n");
StopServer();
return false;
}
if (!segment_pool.Init(numSegments, segmentSize + NormDataMsg::PayloadHeaderLen()))
{
DMSG(0, "NormSession::StartServer() segment_pool init error\n");
StopServer();
return false;
}
if (numParity)
{
if (!encoder.Init(numParity, segmentSize + NormDataMsg::PayloadHeaderLen()))
{
DMSG(0, "NormSession::StartServer() encoder init error\n");
StopServer();
return false;
}
}
segment_size = segmentSize;
ndata = numData;
nparity = numParity;
is_server = true;
flush_count = NORM_ROBUST_FACTOR; // (TBD) parameterize robust_factor
return true;
} // end NormSession::StartServer()
void NormSession::StopServer()
{
encoder.Destroy();
tx_table.Destroy();
block_pool.Destroy();
segment_pool.Destroy();
tx_repair_mask.Destroy();
tx_pending_mask.Destroy();
tx_table.Destroy();
is_server = false;
if (!IsClient()) Close();
} // end NormSession::StopServer()
bool NormSession::StartClient(unsigned long bufferSize)
{
is_client = true;
remote_server_buffer_size = bufferSize;
return true;
}
void NormSession::StopClient()
{
server_tree.Destroy();
is_client = false;
if (!is_server) Close();
}
void NormSession::Serve()
{
if (tx_timer.IsActive())
{
return;
}
NormObject* obj = NULL;
// Queue next server message
if (tx_pending_mask.IsSet())
{
NormObjectId objectId(tx_pending_mask.FirstSet());
obj = tx_table.Find(objectId);
ASSERT(obj);
flush_count = 0;
}
else
{
if (!posted_tx_queue_empty)
{
posted_tx_queue_empty = true;
Notify(NormController::TX_QUEUE_EMPTY,
(NormServerNode*)NULL,
(NormObject*)NULL);
// (TBD) Was session deleted?
Serve();
return;
}
}
if (obj)
{
NormMessage* msg = message_pool.RemoveHead();
if (msg)
{
if (obj->NextServerMsg(msg))
{
msg->generic.SetDestination(address);
QueueMessage(msg);
if (!obj->IsPending())
tx_pending_mask.Unset(obj->Id());
}
else
{
message_pool.Append(msg);
if (obj->IsStream())
{
if (!posted_tx_queue_empty)
{
posted_tx_queue_empty = true;
Notify(NormController::TX_QUEUE_EMPTY,
(NormServerNode*)NULL, obj);
// (TBD) Was session deleted?
Serve();
return;
}
}
else
{
DMSG(0, "NormSession::Serve() pending obj, no message?.\n");
}
}
}
else
{
DMSG(0, "NormSession::Serve() Warning! message_pool empty.\n");
}
}
else if (flush_count < NORM_ROBUST_FACTOR)
{
// Queue flush message
flush_count++;
}
} // end NormSession::Serve()
void NormSession::QueueMessage(NormMessage* msg)
{
if (message_queue.IsEmpty())
{
tx_timer.SetInterval(0.0);
InstallTimer(&tx_timer);
}
message_queue.Append(msg);
} // end NormSesssion::QueueMessage(NormMessage& msg)
NormStreamObject* NormSession::QueueTxStream(UINT32 bufferSize,
const char* infoPtr,
UINT16 infoLen)
{
if (!IsServer())
{
DMSG(0, "NormSession::QueueTxStream() non-server session!\n");
return NULL;
}
NormStreamObject* stream = new NormStreamObject(this, NULL, current_tx_object_id);
if (!stream)
{
DMSG(0, "NormSession::QueueTxStream() new stream error!\n");
return NULL;
}
if (!stream->Open(bufferSize, infoPtr, infoLen))
{
DMSG(0, "NormSession::QueueTxStream() stream open error!\n");
delete stream;
return NULL;
}
if (QueueTxObject(stream, false))
{
// (???: stream has nothing pending until user writes to it???)
//stream->Reset();
return stream;
}
else
{
stream->Close();
delete stream;
return NULL;
}
} // end NormSession::QueueTxStream()
bool NormSession::QueueTxObject(NormObject* obj, bool touchServer)
{
if (!IsServer())
{
DMSG(0, "NormSession::QueueTxObject() non-server session!\n");
return false;
}
// Attempt to queue the object
if (!tx_table.Insert(obj))
{
// (TBD) steal an old non-pending object
DMSG(0, "NormSession::QueueTxObject() tx_table insert error\n");
return false;
}
tx_pending_mask.Set(obj->Id());
ASSERT(tx_pending_mask.Test(obj->Id()));
current_tx_object_id++;
if (touchServer) TouchServer();
return true;
} // end NormSession::QueueTxObject()
NormBlock* NormSession::ServerGetFreeBlock(NormObjectId objectId,
NormBlockId blockId)
{
// First, try to get one from our block pool
NormBlock* b = block_pool.Get();
if (!b)
{
NormObjectTable::Iterator iterator(tx_table);
NormObject* obj;
while ((obj = iterator.GetNextObject()))
{
if (obj->Id() > objectId)
{
break;
}
else
{
if (obj->Id() < objectId)
b = obj->StealOldestBlock(false);
else
b = obj->StealOldestBlock(true, blockId);
if (b)
{
b->EmptyToPool(segment_pool);
break;
}
}
}
}
return b;
} // end NormSession::ServerGetFreeBlock()
char* NormSession::ServerGetFreeSegment(NormObjectId objectId,
NormBlockId blockId)
{
while (segment_pool.IsEmpty() &&
ServerGetFreeBlock(objectId, blockId));
return segment_pool.Get();
} // end NormSession::ServerGetFreeSegment()
bool NormSession::TxSocketRecvHandler(UdpSocket* /*theSocket*/)
{
NormMessage msg;
unsigned int buflen = NORM_MSG_SIZE_MAX;
if (UDP_SOCKET_ERROR_NONE == tx_socket.RecvFrom(msg.generic.GetBuffer(), &buflen, msg.generic.Src()))
{
msg.generic.SetLength(buflen);
HandleReceiveMessage(msg, true);
}
else
{
DMSG(0, "NormSession::TxSocketRecvHandler() recv from error");
}
return true;
} // end NormSession::TxSocketRecvHandler()
bool NormSession::RxSocketRecvHandler(UdpSocket* /*theSocket*/)
{
NormMessage msg;
unsigned int buflen = NORM_MSG_SIZE_MAX;
if (UDP_SOCKET_ERROR_NONE == rx_socket.RecvFrom(msg.generic.GetBuffer(), &buflen, msg.generic.Src()))
{
msg.generic.SetLength(buflen);
HandleReceiveMessage(msg, false);
}
else
{
DMSG(0, "NormSession::RxSocketRecvHandler() recv from error");
}
return true;
} // end NormSession::RxSocketRecvHandler()
void NormSession::HandleReceiveMessage(NormMessage& msg, bool wasUnicast)
{
switch (msg.generic.GetType())
{
case NORM_MSG_INFO:
DMSG(0, "NormSession::HandleReceiveMessage(NORM_MSG_INFO)\n");
break;
case NORM_MSG_DATA:
//DMSG(0, "NormSession::HandleReceiveMessage(NORM_MSG_DATA)\n");
ClientHandleObjectMessage(msg);
break;
case NORM_MSG_CMD:
case NORM_MSG_NACK:
case NORM_MSG_ACK:
case NORM_MSG_REPORT:
case NORM_MSG_INVALID:
DMSG(0, "NormSession::HandleReceiveMessage(NORM_MSG_INVALID)\n");
break;
}
} // end NormSession::HandleReceiveMessage()
void NormSession::ClientHandleObjectMessage(NormMessage& msg)
{
NormNodeId serverId = msg.generic.GetSender();
NormServerNode* theServer = (NormServerNode*)server_tree.FindNodeById(serverId);
if (!theServer)
{
if ((theServer = new NormServerNode(this, serverId)))
{
server_tree.AttachNode(theServer);
DMSG(0, "NormSession::ClientHandleObjectMessage() new remote server:%lu ...\n",
serverId);
}
else
{
DMSG(0, "NormSession::ClientHandleObjectMessage() new server node error\n");
// (TBD) notify application of error
return;
}
}
theServer->SetAddress(*msg.generic.Src());
theServer->HandleObjectMessage(msg);
} // end NormSession::ClientHandleObjectMessage()
bool NormSession::OnTxTimeout()
{
NormMessage* msg = message_queue.RemoveHead();
if (msg)
{
// Fill in common message fields
msg->generic.SetVersion(1);
msg->generic.SetSequence(tx_sequence++);
msg->generic.SetSender(local_node_id);
NetworkAddress* dest = msg->generic.Dest();
// Use session address by default
if (!dest) dest = &address;
UINT16 len = msg->generic.GetLength();
if (UDP_SOCKET_ERROR_NONE != tx_socket.SendTo(dest, msg->generic.GetBuffer(), len))
{
DMSG(0, "NormSession::OnTxTimeout() sendto() error\n");
}
tx_timer.SetInterval(((double)len) / tx_rate);
message_pool.Append(msg);
}
else
{
tx_timer.Deactivate();
if (IsServer()) Serve();
return false;
}
return true;
} // end NormSession::OnTxTimeout()
NormSessionMgr::NormSessionMgr()
: socket_installer(NULL), socket_install_data(NULL),
controller(NULL), top_session(NULL)
{
}
NormSessionMgr::~NormSessionMgr()
{
Destroy();
}
void NormSessionMgr::Destroy()
{
NormSession* next;
while ((next = top_session))
{
top_session = next->next;
delete next;
}
} // end NormSessionMgr::Destroy()
NormSession* NormSessionMgr::NewSession(const char* sessionAddress,
UINT16 sessionPort,
NormNodeId localNodeId)
{
if (NORM_NODE_ANY == localNodeId)
{
// Use local ip address to assign default localNodeId
NetworkAddress localAddr;
if (!localAddr.LookupLocalHostAddress())
{
DMSG(0, "NormSessionMgr::NewSession() local address lookup error\n");
return ((NormSession*)NULL);
}
// (TBD) test IPv6 "EndIdentifier" ???
localNodeId = localAddr.EndIdentifier();
}
NetworkAddress a;
if (!a.LookupHostAddress(sessionAddress))
{
DMSG(0, "NormSessionMgr::NewSession() session address lookup error!\n");
return ((NormSession*)NULL);
}
a.SetPort(sessionPort);
NormSession* s = new NormSession(*this, localNodeId);
if (!s)
{
DMSG(0, "NormSessionMgr::NewSession() new session error: %s\n",
strerror(errno));
return ((NormSession*)NULL);
}
s->SetAddress(a);
// Add new session to our session list
s->next = top_session;
top_session = s;
return s;
} // end NormSessionMgr::NewSession();

208
common/normSession.h Normal file
View File

@ -0,0 +1,208 @@
#ifndef _NORM_SESSION
#define _NORM_SESSION
#include "normMessage.h"
#include "normObject.h"
#include "normNode.h"
#include "normEncoder.h"
#include "protocolTimer.h"
#include "udpSocket.h"
class NormController
{
public:
enum Event
{
TX_QUEUE_EMPTY,
RX_OBJECT_NEW,
RX_OBJECT_UPDATE
};
virtual void Notify(NormController::Event event,
class NormSessionMgr* sessionMgr,
class NormSession* session,
class NormServerNode* server,
class NormObject* object) = 0;
}; // end class NormController
class NormSessionMgr
{
friend class NormSession;
public:
NormSessionMgr();
~NormSessionMgr();
void Init(ProtocolTimerInstallFunc* timerInstaller,
const void* timerInstallData,
UdpSocketInstallFunc* socketInstaller,
void* socketInstallData,
NormController* theController)
{
timer_mgr.SetInstaller(timerInstaller, timerInstallData);
socket_installer = socketInstaller;
socket_install_data = socketInstallData;
controller = theController;
}
void Destroy();
class NormSession* NewSession(const char* sessionAddress,
UINT16 sessionPort,
NormNodeId localNodeId =
NORM_NODE_ANY);
UdpSocketInstallFunc* SocketInstaller() {return socket_installer;}
const void* SocketInstallData() {return socket_install_data;}
void Notify(NormController::Event event,
class NormSession* session,
class NormServerNode* server,
class NormObject* object)
{
if (controller)
controller->Notify(event, this, session, server, object);
}
private:
void InstallTimer(ProtocolTimer* timer)
{timer_mgr.InstallTimer(timer);}
ProtocolTimerMgr timer_mgr;
UdpSocketInstallFunc* socket_installer;
const void* socket_install_data;
NormController* controller;
class NormSession* top_session;
}; // end class NormSessionMgr
class NormSession
{
friend class NormSessionMgr;
public:
enum {DEFAULT_MESSAGE_POOL_DEPTH = 16};
static const double DEFAULT_TRANSMIT_RATE; // in bytes per second
NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId);
~NormSession();
// General methods
const NormNodeId& LocalNodeId() {return local_node_id;}
bool Open();
void Close();
bool IsOpen() {return (rx_socket.IsOpen() || tx_socket.IsOpen());}
void SetAddress(const NetworkAddress& addr) {address = addr;}
void SetLoopback(bool state)
{rx_socket.SetLoopback(state);
tx_socket.SetLoopback(state);}
void Notify(NormController::Event event,
class NormServerNode* server,
class NormObject* object)
{
session_mgr.Notify(event, this, server, object);
}
// Server methods
bool StartServer(unsigned long bufferSpace,
UINT16 segmentSize,
UINT16 numData,
UINT16 numParity);
void StopServer();
bool IsServer() {return is_server;}
UINT16 ServerSegmentSize() {return segment_size;}
UINT16 ServerBlockSize() {return ndata;}
UINT16 ServerNumParity() {return nparity;}
UINT16 ServerAutoParity() {return auto_parity;}
void ServerSetAutoParity(UINT16 autoParity)
{ASSERT(autoParity <= nparity); auto_parity = autoParity;}
void ServerEncode(const char* segment, char** parityVectorList)
{encoder.Encode(segment, parityVectorList);}
NormStreamObject* QueueTxStream(UINT32 bufferSize,
const char* infoPtr = NULL,
UINT16 infoLen = 0);
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
char* ServerGetFreeSegment(NormObjectId objectId, NormBlockId blockId);
void ServerPutFreeBlock(NormBlock* block)
{
block->EmptyToPool(segment_pool);
block_pool.Put(block);
}
void TouchServer()
{
posted_tx_queue_empty = false;
Serve();
}
// Client methods
bool StartClient(unsigned long bufferSpace);
void StopClient();
bool IsClient() {return is_client;}
unsigned long RemoteServerBufferSize()
{return remote_server_buffer_size;}
private:
void QueueMessage(NormMessage* msg);
void Serve();
bool QueueTxObject(NormObject* obj, bool touchServer = true);
void InstallTimer(ProtocolTimer* timer)
{session_mgr.InstallTimer(timer);}
bool OnTxTimeout();
bool TxSocketRecvHandler(UdpSocket* theSocket);
bool RxSocketRecvHandler(UdpSocket* theSocket);
void HandleReceiveMessage(NormMessage& msg, bool wasUnicast);
void ClientHandleObjectMessage(NormMessage& msg);
NormSessionMgr& session_mgr;
ProtocolTimer tx_timer;
UdpSocket tx_socket;
UdpSocket rx_socket;
NormMessageQueue message_queue;
NormMessageQueue message_pool;
// General session parameters
NormNodeId local_node_id;
NetworkAddress address; // session destination address
UINT8 ttl; // session multicast ttl
UINT16 tx_sequence;
double tx_rate; // bytes per second
// Server parameters
bool is_server;
UINT16 segment_size;
UINT16 ndata;
UINT16 nparity;
UINT16 auto_parity;
NormObjectTable tx_table;
NormSlidingMask tx_pending_mask;
NormSlidingMask tx_repair_mask;
NormBlockPool block_pool;
NormSegmentPool segment_pool;
NormEncoder encoder;
NormObjectId current_tx_object_id;
int flush_count;
bool posted_tx_queue_empty;
// Client parameters
bool is_client;
NormNodeTree server_tree;
unsigned long remote_server_buffer_size;
// Misc
NormSession* next;
}; // end class NormSession
#endif // _NORM_SESSION

85
common/rules.txt Normal file
View File

@ -0,0 +1,85 @@
NORM Rules
This file contains descriptions of rules used in the NRL
NORM implementation for various aspects of protocol
operation. These rules are related to the nature of the
NORM protocol and how this particular implementation
maintains protocol state.
Norm Receive Object Status Rules
====================================
When a message for a transport object is received from a
remote NORM server node, the "status" (based on the object's
transport identifier - NormObjectId)of the object is
determined so that appropriate actions may be taken by the
receiver. The possible status types include:
INVALID - the NormObjectId is out-of-range with respect
to the current state for the sender. Out-of-
range is defined as an objectId which is
excessively ordinally less than the range of
currently pending objects (or last object
successfully complete if none are pending)
Note that if the object is the _first_ object
received for the sender, it is always considered
"valid", thus establishing an initial
synchronization point for the sender (sync_id):
sender->Synchronized AND
objectId < sync_id OR
objectId > first_pending + bufferRange
(first_pending = sender->IsPending ?
first_pending : next_pending)
NEW - The objectId is greater than the range of
currently pending objects (but not too
much greater) and acceptable for reception.
Note that is the object is the _first_
received from the sender, this status
always results:
!sender->Synchronized OR
objectId >= next_pending AND
object_id - first_pending < bufferRange
(first_pending = sender->IsPending ?
first_pending : next_pending)
PENDING - The objectId corresponds to an object _within_
the range of currently pending objects for
the sender and has is marked as still pending:
sender->Synchronized AND
sender->IsPending() AND
first_pending <= objectId < next_pending AND
sender->IsPending(objectId)
COMPLETE - The objectId is within range of objects which
have been detected and is not marked as pending:
sender->Synchronized AND
sync_id <= objectId < next_pending AND
!sender->IsPending(objectId)
Note since the sequence of objectId's received from a sender
is circular, the "sync_id" will eventually need to be
updated as the sequence of objects progresses. Also note
that the "sync_id" might be adjusted depending upon the
receiver synchronisation policy. For example, if the
synchronization policy is strict, the "sync_id" will be
fixed to no less than the first object the receiver accepts
for reception (according to policies) But for a looser
policy the receiver might permit the sync_id to be
decremented to fit within the current "bufferRange". An
even looser policy would be to allow the receiver's buffer
range to grow as needed. However, for some applications,
the sender has a finite range of objects for which it
will maintain repair state.
The "bufferRange" is the range (sequential count) of objects
for which the receiver is maintaining state. That range may
be application specific and senders/receivers are anticipated
to use relatively compatible buffer ranges/sizes based on
application needs.

81
unix/Makefile.common Normal file
View File

@ -0,0 +1,81 @@
#########################################################################
# COMMON MDP MAKEFILE STUFF
#
SHELL=/bin/sh
.SUFFIXES: .cpp $(.SUFFIXES)
PROTOLIB = ../protolib
COMMON = ../common
UNIX = ./
NS = ../ns
INCLUDES = $(TCL_INCL_PATH) $(SYSTEM_INCLUDES) -I$(UNIX) -I$(COMMON) -I$(PROTOLIB)/common
CFLAGS = -g -DPROTO_DEBUG -DUNIX -Wall -O -fPIC $(SYSTEM_HAVES) $(INCLUDES)
LDFLAGS = $(SYSTEM_LDFLAGS)
# Note: Even command line app needs X11 for Netscape post-processing
LIBS = $(SYSTEM_LIBS) -lm
XLIBS = -lXmu -lXt -lX11
#NOTE: TK_LIB must come before TCL_LIB for some reason
TKLIBS = $(TK_LIB) $(TCL_LIB)
TCL_SCRIPTS = ${TCL_SCRIPT_PATH}/init.tcl \
${TK_SCRIPT_PATH}/tk.tcl \
${TK_SCRIPT_PATH}/bgerror.tcl \
${TK_SCRIPT_PATH}/button.tcl \
${TK_SCRIPT_PATH}/dialog.tcl \
${TK_SCRIPT_PATH}/entry.tcl \
${TK_SCRIPT_PATH}/focus.tcl \
${TK_SCRIPT_PATH}/listbox.tcl \
${TK_SCRIPT_PATH}/menu.tcl \
${TK_SCRIPT_PATH}/palette.tcl \
${TK_SCRIPT_PATH}/scale.tcl \
${TK_SCRIPT_PATH}/scrlbar.tcl \
${TK_SCRIPT_PATH}/tearoff.tcl \
${TK_SCRIPT_PATH}/text.tcl \
${TK_SCRIPT_PATH}/optMenu.tcl
TARGETS = mdp tkMdp
# Rule for C++ .cpp extension
.cpp.o:
$(CC) -c $(CFLAGS) -o $*.o $*.cpp
# MDP depends upon the NRL Protean Group's development library
LIBPROTO = $(PROTOLIB)/unix/libProto.a
$(PROTOLIB)/unix/libProto.a:
make -C $(PROTOLIB)/unix -f Makefile.common libProto.a
NORM_SRC = $(COMMON)/normMessage.cpp $(COMMON)/normSession.cpp \
$(COMMON)/normNode.cpp $(COMMON)/normObject.cpp \
$(COMMON)/normSegment.cpp $(COMMON)/normBitmask.cpp \
$(COMMON)/normEncoder.cpp $(COMMON)/galois.cpp
NORM_OBJ = $(NORM_SRC:.cpp=.o)
LIB_SRC = $(NORM_SRC)
LIB_OBJ = $(LIB_SRC:.cpp=.o)
libNorm.a: $(LIB_OBJ)
ar rcv $@ $(LIB_OBJ)
ranlib $@
# (mdp) command-line file broadcaster/receiver
APP_SRC = $(COMMON)/normApp.cpp
APP_OBJ = $(APP_SRC:.cpp=.o)
norm: $(APP_OBJ) libNorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(APP_OBJ) $(LDFLAGS) $(LIBS) libNorm.a $(LIBPROTO)
clean:
rm -f $(COMMON)/*.o $(UNIX)/*.o $(UNIX)/libNorm.a $(UNIX)/norm;
make -C $(PROTOLIB)/unix -f Makefile.common clean
# DO NOT DELETE THIS LINE -- mkdep uses it.
# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.

69
unix/Makefile.freebsd Normal file
View File

@ -0,0 +1,69 @@
#
# FreeBSD Protean Makefile definitions
#
# TO BUILD THE TK GUI VERSION, EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include/tcl8.0 -I/usr/local/include/tk8.0
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl80.a
TK_LIB = /usr/local/lib/libtk80.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/X11R6/include
SYSTEM_LDFLAGS = -L/usr/X11R6/lib
SYSTEM_LIBS =
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_GETLOGIN -DHAVE_FLOCK -DHAVE_DIRFD $(DNETSEC)
export CC = gcc
include Makefile.common

69
unix/Makefile.hpux Normal file
View File

@ -0,0 +1,69 @@
#
# HPUX Protean Makefile definitions
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /opt/tcl-8.0/lib
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /opt/tk-8.0/lib/X11
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/opt/tcl-8.0/include -I/opt/tk-8.0/include/X11
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = ./hpux-libtcl8.0.a
TK_LIB = ./hpux-libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES =
SYSTEM_LDFLAGS = -Xlinker +b -Xlinker /usr/lib/X11R5:/usr/lib/X11R6:/usr/local/X11R5/lib:/usr/local/X11R6/lib:/usr/lib:/usr/lib/X11R4\
-Xlinker +s -L/home/macker/mdp/X11R5/lib
SYTSTEM_LIBS =
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_CUSERID -DHAVE_LOCKF
export CC = gcc
include Makefile.common

70
unix/Makefile.linux Normal file
View File

@ -0,0 +1,70 @@
#
# Linux Protean Makefile definitions
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl8.0.a
TK_LIB = /usr/local/lib/libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/X11R6/include
SYSTEM_LDFLAGS = -L/usr/X11R6/lib
SYSTEM_LIBS = -ldl
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -DHAVE_FLOCK for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if your system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DLINUX -DHAVE_IPV6 -DHAVE_GETLOGIN -DHAVE_LOCKF \
-DHAVE_OLD_SIGNALHANDLER -DHAVE_DIRFD -DHAVE_ASSERT $(NETSEC)
export CC = gcc
include Makefile.common

69
unix/Makefile.macosx Normal file
View File

@ -0,0 +1,69 @@
#
# MacOS X Protean Makefile definitions
#
# TO BUILD THE TK GUI VERSION, EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include/tcl8.0 -I/usr/local/include/tk8.0
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl80.a
TK_LIB = /usr/local/lib/libtk80.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/X11R6/include
SYSTEM_LDFLAGS = -L/usr/X11R6/lib
SYSTEM_LIBS =
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_ASSERT -DHAVE_GETLOGIN -DHAVE_FLOCK -DHAVE_DIRFD $(DNETSEC)
export CC = cc
include Makefile.common

71
unix/Makefile.mklinux Normal file
View File

@ -0,0 +1,71 @@
#
# MDPv2 NetBSD Makefile
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl8.0.a
TK_LIB = /usr/local/lib/libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/X11R6/include
SYSTEM_LDFLAGS = -L/usr/X11R6/lib
SYSTEM_LIBS = -ldl
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_CUSERID -DHAVE_LOCKF -DHAVE_DIRFD $(NETSEC)
export CC = gcc
include Makefile.common

69
unix/Makefile.netbsd Normal file
View File

@ -0,0 +1,69 @@
#
# MDPv2 NetBSD Makefile
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl80.a
TK_LIB = /usr/local/lib/libtk80.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/X11R6/include
SYSTEM_LDFLAGS = -L/usr/X11R6/lib
SYSTEM_LIBS =
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_GETLOGIN -DHAVE_ASSERT -DHAVE_FLOCK -DHAVE_DIRFD $(DNETSEC)
export CC = gcc
include Makefile.common

69
unix/Makefile.sgi Normal file
View File

@ -0,0 +1,69 @@
#
# MDPv2 IRIX Makefile
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl8.0.a
TK_LIB = /usr/local/lib/libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES =
SYSTEM_LDFLAGS =
SYSTEM_LIBS =
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DUSE_INHERITANCE -DHAVE_CUSERID -DHAVE_LOCKF $(NETSEC)
export CC = gcc
include Makefile.common

67
unix/Makefile.solaris Normal file
View File

@ -0,0 +1,67 @@
#
# MDPv2 Solaris Makefile
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl8.0.a
TK_LIB = /usr/local/lib/libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/openwin/include
SYSTEM_LDFLAGS = -L/usr/openwin/lib -R/usr/openwin/lib
SYSTEM_LIBS = -ldl -lnsl -lsocket
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_ASSERT -DHAVE_CUSERID -DHAVE_LOCKF $(NETSEC) -DSOLARIS
export CC = gcc
include Makefile.common

68
unix/Makefile.solx86 Normal file
View File

@ -0,0 +1,68 @@
#
# MDPv2 Solaris x86 (Solaris Version 2.7) Makefile
#
# TO BUILD THE TK GUI VERSION OF MDP EDIT THE FOLLOWING NUMBERED
# ITEMS AS NEEDED FOR YOUR SYSTEM
# (This has only been tested with TCL/TK 8.0 but it probably
# will work with Tcl7.x/Tk4.x with a little tweaking to
# the list of TCL_SCRIPTS (library scripts) given below)
# 1) Where to find the Tcl standard library scripts
# (e.g. init.tcl, ...)
TCL_SCRIPT_PATH = /usr/local/lib/tcl8.0
# 2) Where to find the Tk standard library scripts
# (e.g. button.tcl, entry.tcl, ...)
TK_SCRIPT_PATH = /usr/local/lib/tk8.0
# 3) Where to find Tcl/Tk header files
# (e.g. tcl.h, tk.h, ...)
TCL_INCL_PATH = -I/usr/local/include
# 4) Point to specific libtcl.a and libtk.a to use
TCL_LIB = /usr/local/lib/libtcl8.0.a
TK_LIB = /usr/local/lib/libtk8.0.a
# 5) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/openwin/include
SYSTEM_LDFLAGS = -L/usr/openwin/lib -R/usr/openwin/lib
SYSTEM_LIBS = -ldl -lnsl -lsocket
# 6) System specific capabilities
# Must choose appropriate for the following:
#
# A) -DHAVE_CUSERID (preferred) or -DHAVE_GETLOGIN for cuserid() or getlogin()
# functions to obtain user's login name (We may change this to getpwd()
# if that is better across different platforms and login environments)
#
# B) -DHAVE_LOCKF (preferred) or -D_LOCKF for lockf() or flock() file locking
# functions to attempt exclusive lock on writing to files
#
# C) Specify -DHAVE_DIRFD if you system provides the "dirfd()" function
# (Most don't have it defined ... but some do)
#
# D) Optionally specify -DHAVE_ASSERT if your system has a built-in ASSERT()
# routine.
#
# E) The MDP code's use of offset pointers requires special treatment
# for some different compilers. Set -DUSE_INHERITANCE for some
# to keep some compilers (gcc 2.7.2) happy.
#
# F) Some systems (SOLARIS/SUNOS) have a few gotchas which require
# some #ifdefs to avoid compiler warnings ... so you might need
# to specify -DSOLARIS or -DSUNOS depending on your OS.
#
# G) Uncomment this if you have the NRL IPv6+IPsec software
#DNETSEC = -DNETSEC -I/usr/inet6/include
#
# (We export these for other Makefiles as needed)
#
export SYSTEM_HAVES = -DHAVE_ASSERT -DHAVE_CUSERID -DHAVE_LOCKF $(NETSEC) -DSOLARIS
export CC = gcc
include Makefile.common