v0.1
commit
565afa5529
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
********************************************************************/
|
||||||
|
|
@ -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.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||||
|
|
@ -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()
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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_
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
Loading…
Reference in New Issue