pull/2/head
Jeff Weston 2019-09-11 11:59:50 -04:00
parent 276f7c4e20
commit 28c6631d92
47 changed files with 8297 additions and 3111 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -55,9 +55,20 @@
19) Fix "activity_timer" interval setting (COMPLETED)
20) Double-check watermark check code around line 600 of normSession.cpp
20) Fix issue related to setting tx cache count_max > 256!
Fix NormSession::SetTxCacheBounds() ... tx_table may need to be
resized accordingly (COMPLETED, but needs testing).
21) Double-check watermark check code around line 600 of normSession.cpp
(COMPLETED - reimplemented, adding tx_repair_pending index to use
instead of seeking each time)
22) Implement LDPC FEC code within NORM as alternative to Reed Solomon
23) Add ability to control receiver cache on a per-sender basis?
(max_pending_range, etc)
24) Look at NormStreamObject::StreamAdvance() for "push-enabled" streams
(COMPLETED)

View File

@ -1,5 +1,30 @@
NORM Version History
Version 1.4b1
=============
- Fixed condition where receivers kept state for remote senders "stuck"
with CC feedback (cc_enable) even if newer NORM_CMD(CC) messages
indicate that sender CC operation has been disabled.
- Fixed error in NormSession::SetTOS() method that transmogrifried the
default TTL value into TOS under certain conditions.
(thanks to Ron in 't Velt yet again for finding this and the one above).
- Made NormFile read/write more persistent (e.g. when EINT occurs)
- Fixed NormObject::PassiveRepairCheck() bug that incorrectly reported
no repair needed when in fact, the last segment was missing.
- Fixed ProtoSlidingBitmask error that interfered with proper
sender stream advancement under severe conditions
- NORM still used old "MDP" FEC codecs but now explicitly check for
FEC ID == 129 and FEC instance id == 0. Can be built with
-DASSUME_MDP_FEC to avoid this check in case of backward compatbility
issue. Future NORM release will be using new FEC implementation
for Reed-Solomon coding but will continue to support compile-time
backwards compatibility option to use old MDP FEC encoder.
- Added "lowDelay" and other options to "norm" demo application
- Modified "lowDelay" option for silent receiver use to allow
control of FEC block delay (how many blocks to wait before passing
partially-received coding blocks to application).
- Added "Norm Pre-Coder" (NPC) utility to distribution.
Version 1.3b9
=============
- Fixed ProtoSocket WINVER stuff and NormApplicationMsg::SetContent()

View File

@ -491,9 +491,14 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent)
}
break;
case NORM_RX_OBJECT_UPDATED:
// reset update event notification
((NormObject*)n->event.object)->SetNotifyOnUpdate(true);
{
// reset update event notification for non-streams
// (NormStreamRead() takes care of streams)
NormObject* obj = ((NormObject*)n->event.object);
if (!obj->IsStream())
obj->SetNotifyOnUpdate(true);
break;
}
default:
break;
}
@ -1502,13 +1507,13 @@ bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle,
NORM_API_LINKAGE
void NormSetSilentReceiver(NormSessionHandle sessionHandle,
bool silent,
bool lowDelay)
INT32 maxDelay)
{
NormSession* session = (NormSession*)sessionHandle;
if (session)
{
session->ClientSetSilent(silent);
session->ReceiverSetLowDelay(lowDelay);
session->RcvrSetMaxDelay(maxDelay);
}
} // end NormSetSilentReceiver()

View File

@ -393,7 +393,7 @@ bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle,
NORM_API_LINKAGE
void NormSetSilentReceiver(NormSessionHandle sessionHandle,
bool silent,
bool lowDelay = false);
int maxDelay = -1);
NORM_API_LINKAGE
void NormSetDefaultUnicastNack(NormSessionHandle sessionHandle,

View File

@ -93,6 +93,8 @@ class NormApp : public NormController, public ProtoApp
bool loopback;
char* interface_name; // for multi-home hosts
double tx_rate; // bits/sec
double tx_rate_min;
double tx_rate_max;
bool cc_enable;
// NormSession server-only parameters
@ -107,6 +109,13 @@ class NormApp : public NormController, public ProtoApp
unsigned long tx_buffer_size; // bytes
NormFileList tx_file_list;
bool tx_one_shot;
bool tx_ack_shot;
bool tx_file_queued;
// save last obj/block/seg id for later in case needed
NormObjectId tx_last_object_id;
NormBlockId tx_last_block_id;
NormSegmentId tx_last_segment_id;
double tx_object_interval;
int tx_repeat_count;
double tx_repeat_interval;
@ -123,6 +132,8 @@ class NormApp : public NormController, public ProtoApp
NormPostProcessor* post_processor;
bool unicast_nacks;
bool silent_client;
bool low_delay;
bool process_aborted_files;
// Debug parameters
bool tracing;
@ -140,16 +151,17 @@ NormApp::NormApp()
input_messaging(false), input_msg_length(0), input_msg_index(0),
output_index(0), output_messaging(false), output_msg_length(0), output_msg_sync(false),
address(NULL), port(0), ttl(32), loopback(false), interface_name(NULL),
tx_rate(64000.0), cc_enable(false),
tx_rate(64000.0), tx_rate_min(-1.0), tx_rate_max(-1.0), cc_enable(false),
segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0),
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE),
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
tx_buffer_size(1024*1024), tx_one_shot(false),
tx_buffer_size(1024*1024), tx_one_shot(false), tx_ack_shot(false), tx_file_queued(false),
tx_object_interval(0.0), tx_repeat_count(0), tx_repeat_interval(2.0), tx_repeat_clear(true),
acking_node_list(NULL), watermark_pending(false),
rx_buffer_size(1024*1024), rx_sock_buffer_size(0),
rx_cache_path(NULL), post_processor(NULL), unicast_nacks(false), silent_client(false),
low_delay(false), process_aborted_files(false),
tracing(false), tx_loss(0.0), rx_loss(0.0)
{
@ -209,6 +221,7 @@ const char* const NormApp::cmd_list[] =
"+interface", // multicast interface name to use
"+cc", // congestion control on/off
"+rate", // tx date rate (bps)
"+limit", // tx rate limits <rateMin:rateMax>
"-push", // push stream writes for real-time messaging
"+flush", // message flushing mode ("none", "passive", or "active")
"+input", // send stream input
@ -220,6 +233,7 @@ const char* const NormApp::cmd_list[] =
"+repeatcount", // How many times to repeat the file/directory list tx
"+rinterval", // Interval (sec) between file/directory list repeats
"-oneshot", // Transmit file(s), exiting upon TX_FLUSH_COMPLETED
"-ackshot", // Transmit file(s), exiting upon TX_WATERMARK_COMPLETED
"-updatesOnly", // only send updated files on repeat transmission
"+ackingNodes", // comma-delimited list of node id's to from which to collect acks
"+rxcachedir", // recv file cache directory
@ -235,7 +249,9 @@ const char* const NormApp::cmd_list[] =
"+rxbuffer", // Size receiver allocates for buffering each sender
"+rxsockbuffer", // Optional recv socket buffer size.
"-unicastNacks", // unicast instead of multicast feedback messages
"-silentClient", // "silent" (non-nacking) client (EMCON mode)
"-silentClient", // "silent" (non-nacking) receiver (EMCON mode) (must set for sender too)
"-lowDelay", // for silent receivers only, delivers data/files to app sooner\n"
"-saveAborts", // save (and possibly post-process) aborted receive files\n"
"+processor", // receive file post processing command
"+instance", // specify norm instance name for remote control commands
NULL
@ -261,6 +277,7 @@ void NormApp::ShowHelp()
" +interface, // multicast interface name to use\n"
" +cc, // congestion control 'on' or 'off'\n"
" +rate, // tx date rate (bps)\n"
" +limit // tx rate bounds <rateMin:rateMax>\n"
" -push, // push stream writes for real-time messaging\n"
" +flush, // message flushing mode ('none', 'passive', or 'active')\n"
" +input, // send stream input\n"
@ -272,6 +289,7 @@ void NormApp::ShowHelp()
" +repeatcount, // How many times to repeat the file/directory list tx\n"
" +rinterval, // Interval (sec) between file/directory list repeats\n"
" -oneshot, // Exit upon sender TX_FLUSH_COMPLETED event (sender exits after transmission)\n"
" -ackshot, // Exit upon sender TX_WATERMARK_COMPLETED event (sender exits after transmission)\n"
" -updatesOnly, // only send updated files on repeat transmission\n"
" +ackingNodes, // comma-delimited list of node id's to from which to collect acks\n"
" +rxcachedir, // recv file cache directory\n"
@ -287,7 +305,9 @@ void NormApp::ShowHelp()
" +rxbuffer, // Size receiver allocates for buffering each sender\n"
" +rxsockbuffer, // Optional recv socket buffer size.\n"
" -unicastNacks, // unicast instead of multicast feedback messages\n"
" -silentClient, // silent (non-nacking) receiver (EMCON mode)\n"
" -silentClient, // silent (non-nacking) receiver (EMCON mode) (must set for sender too)\n"
" -lowDelay, // for silent receivers only, delivers data/files to app sooner\n"
" -saveAborts, // save (and possibly post-process) aborted receive files\n"
" +processor, // receive file post processing command\n"
" +instance, // specify norm instance name for remote control commands\n"
"\n");
@ -527,6 +547,18 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
tx_rate = txRate;
if (session) session->SetTxRate(txRate);
}
else if (!strncmp("rate", cmd, len))
{
double rateMin, rateMax;
if (2 != sscanf(val, "%lf:%lf", &rateMin, &rateMax))
{
DMSG(0, "NormApp::OnCommand(rate) invalid txRate limits!\n");
return false;
}
tx_rate_min = rateMin;
tx_rate_max = rateMax;
if (session) session->SetTxRateBounds(rateMin, rateMax);
}
else if (!strncmp("cc", cmd, len))
{
if (!strcmp("on", val))
@ -657,6 +689,11 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
{
tx_one_shot = true;
}
else if (!strncmp("ackshot", cmd, len))
{
tx_ack_shot = true;
}
else if (!strncmp("updatesOnly", cmd, len))
{
tx_file_list.InitUpdateTime(true);
@ -823,7 +860,20 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
else if (!strncmp("silentClient", cmd, len))
{
silent_client = true;
if (session) session->ClientSetSilent(true);
if (session)
{
session->ClientSetSilent(true);
session->SndrSetEmcon(true);
}
}
else if (!strncmp("lowDelay", cmd, len))
{
low_delay = true;
if (session) session->RcvrSetMaxDelay(1);
}
else if (!strncmp("saveAborts", cmd, len))
{
process_aborted_files = true;
}
else if (!strncmp("push", cmd, len))
{
@ -1189,6 +1239,7 @@ void NormApp::Notify(NormController::Event event,
switch (event)
{
case TX_QUEUE_VACANCY:
DMSG(6, "NormApp::Notify(TX_QUEUE_VACANCY) ...\n");
if (NULL != object)
{
if (input && (object == tx_stream))
@ -1197,7 +1248,7 @@ void NormApp::Notify(NormController::Event event,
break;
case TX_QUEUE_EMPTY:
// Write to stream as needed
DMSG(3, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
DMSG(6, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
if (NULL != object)
{
if (input && (object == tx_stream))
@ -1215,11 +1266,11 @@ void NormApp::Notify(NormController::Event event,
break;
case TX_OBJECT_SENT:
DMSG(3, "NormApp::Notify(TX_OBJECT_SENT) ...\n");
DMSG(4, "NormApp::Notify(TX_OBJECT_SENT) ...\n");
break;
case TX_FLUSH_COMPLETED:
DMSG(3, "NormApp::Notify(TX_FLUSH_COMPLETED) ...\n");
DMSG(4, "NormApp::Notify(TX_FLUSH_COMPLETED) ...\n");
if (tx_one_shot)
{
DMSG(0, "norm: transmit flushing completed, exiting.\n");
@ -1229,18 +1280,27 @@ void NormApp::Notify(NormController::Event event,
case TX_WATERMARK_COMPLETED:
{
DMSG(3, "NormApp::Notify(TX_WATERMARK_COMPLETED) ...\n");
NormSession::AckingStatus status = session->ServerGetAckingStatus(NORM_NODE_ANY);
DMSG(0, "norm: positive ack collection completed (%s)\n",
(NormSession::ACK_SUCCESS == status) ? "succeed" : "failed");
DMSG(4, "NormApp::Notify(TX_WATERMARK_COMPLETED) ...\n");
//NormSession::AckingStatus status = session->ServerGetAckingStatus(NORM_NODE_ANY);
watermark_pending = false; // enable new watermark to be set
if (tx_ack_shot)
{
DMSG(0, "norm: transmit flushing completed, exiting.\n");
Stop();
}
break;
}
case RX_OBJECT_NEW:
{
DMSG(3, "NormApp::Notify(RX_OBJECT_NEW) ...\n");
DMSG(4, "NormApp::Notify(RX_OBJECT_NEW) ...\n");
struct timeval currentTime;
ProtoSystemTime(currentTime);
struct tm* timePtr = gmtime((time_t*)&currentTime.tv_sec);
DMSG(3, "%02d:%02d:%02d.%06lu start rx object>%hu sender>%lu\n",
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
// It's up to the app to "accept" the object
switch (object->GetType())
{
@ -1342,7 +1402,7 @@ void NormApp::Notify(NormController::Event event,
}
case RX_OBJECT_INFO:
DMSG(3, "NormApp::Notify(RX_OBJECT_INFO) ...\n");
DMSG(4, "NormApp::Notify(RX_OBJECT_INFO) ...\n");
switch(object->GetType())
{
case NormObject::FILE:
@ -1383,7 +1443,7 @@ void NormApp::Notify(NormController::Event event,
break;
case RX_OBJECT_UPDATED:
DMSG(3, "NormApp::Notify(RX_OBJECT_UPDATED) ...\n");
DMSG(6, "NormApp::Notify(RX_OBJECT_UPDATED) ...\n");
switch (object->GetType())
{
case NormObject::FILE:
@ -1442,7 +1502,10 @@ void NormApp::Notify(NormController::Event event,
DMSG(0, "NormApp::Notify() detected broken stream ...\n");
output_msg_length = output_index = 0;
output_msg_sync = false;
if (findMsgSync)
break;
else
continue; // try to re-sync
}
}
else if (readLength > 0)
@ -1479,6 +1542,7 @@ void NormApp::Notify(NormController::Event event,
while (put < writeLength)
{
size_t result = fwrite(output_buffer+put, sizeof(char), writeLength-put, output);
if (result)
{
put += (unsigned int)result;
@ -1526,7 +1590,13 @@ void NormApp::Notify(NormController::Event event,
// (TBD) if we're not archiving files we should
// manage our cache, deleting the cache
// on shutdown ...
DMSG(3, "NormApp::Notify(RX_OBJECT_COMPLETED) ...\n");
DMSG(4, "NormApp::Notify(RX_OBJECT_COMPLETED) ...\n");
struct timeval currentTime;
ProtoSystemTime(currentTime);
struct tm* timePtr = gmtime((time_t*)&currentTime.tv_sec);
DMSG(3, "%02d:%02d:%02d.%06lu completed rx object>%hu sender>%lu\n",
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
switch(object->GetType())
{
case NormObject::FILE:
@ -1554,6 +1624,45 @@ void NormApp::Notify(NormController::Event event,
}
break;
}
case RX_OBJECT_ABORTED:
{
DMSG(0, "NormApp::Notify(RX_OBJECT_ABORTED) ...\n");
struct timeval currentTime;
ProtoSystemTime(currentTime);
struct tm* timePtr = gmtime((time_t*)&currentTime.tv_sec);
DMSG(3, "%02d:%02d:%02d.%06lu aborted rx object>%hu sender>%lu\n",
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
switch(object->GetType())
{
case NormObject::FILE:
{
const char* filePath = static_cast<NormFileObject*>(object)->GetPath();
if (process_aborted_files)
{
// in case file size isn't padded properly
static_cast<NormFileObject*>(object)->PadToSize();
if (post_processor->IsEnabled())
{
if (!post_processor->ProcessFile(filePath))
{
DMSG(0, "norm: post processing error\n");
}
}
}
else
{
NormFile::Unlink(filePath);
}
break;
}
default:
break;
}
break;
}
default:
{
DMSG(4, "NormApp::Notify() unhandled event: %d\n", event);
@ -1600,13 +1709,25 @@ bool NormApp::OnIntervalTimeout(ProtoTimer& /*theTimer*/)
ActivateTimer(interval_timer);
return false;
}
if (!watermark_pending && (NULL != acking_node_list))
tx_file_queued = true;
struct timeval currentTime;
ProtoSystemTime(currentTime);
struct tm* timePtr = gmtime((time_t*)&currentTime.tv_sec);
DMSG(3, "%02d:%02d:%02d.%06lu enqueued tx object>%hu sender>%lu\n",
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
(unsigned long)currentTime.tv_usec, (UINT16)obj->GetId(), session->LocalNodeId());
// save last obj/block/seg id for later in case needed
tx_last_object_id = obj->GetId();
tx_last_block_id = obj->GetFinalBlockId();
tx_last_segment_id = obj->GetBlockSize(tx_last_block_id) - 1;
if (!watermark_pending && (NULL != acking_node_list) && !tx_ack_shot)
{
NormBlockId blockId = obj->GetFinalBlockId();
NormSegmentId segmentId = obj->GetBlockSize(blockId) - 1;
session->ServerSetWatermark(obj->GetId(),
blockId,
segmentId);
session->ServerSetWatermark(tx_last_object_id,
tx_last_block_id,
tx_last_segment_id);
watermark_pending = true; // only allow one pending watermark at a time
}
//DMSG(0, "norm: File \"%s\" queued for transmission.\n", fileName);
@ -1645,6 +1766,13 @@ bool NormApp::OnIntervalTimeout(ProtoTimer& /*theTimer*/)
{
tx_repeat_clear = true;
DMSG(0, "norm: End of tx file list reached.\n");
if (tx_ack_shot && !watermark_pending && tx_file_queued)
{
session->ServerSetWatermark(tx_last_object_id,
tx_last_block_id,
tx_last_segment_id);
watermark_pending = true; // only allow one pending watermark at a time
}
}
return true;
} // end NormApp::OnIntervalTimeout()
@ -1695,6 +1823,7 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
// Common session parameters
session->SetTxPort(tx_port);
session->SetTxRate(tx_rate);
session->SetTxRateBounds(tx_rate_min, tx_rate_max);
session->SetTrace(tracing);
session->SetTxLoss(tx_loss);
session->SetRxLoss(rx_loss);
@ -1715,7 +1844,8 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
session_mgr.Destroy();
return false;
}
// redundant info transmission if we have silent clients
if (silent_client) session->SndrSetEmcon(true);
// We also use the baseId as our server's "instance id" for illustrative purposes
UINT16 instanceId = baseId;
if (!session->StartServer(instanceId, tx_buffer_size, segment_size, ndata, nparity, interface_name))
@ -1746,6 +1876,10 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
// StartClient(bufferMax (per-sender))
session->ClientSetUnicastNacks(unicast_nacks);
session->ClientSetSilent(silent_client);
if (low_delay)
session->RcvrSetMaxDelay(1);
else
session->RcvrSetMaxDelay(-1);
if (!session->StartClient(rx_buffer_size, interface_name))
{
DMSG(0, "NormApp::OnStartup() start client error!\n");

View File

@ -41,23 +41,35 @@
#include "normMessage.h"
#endif // SIMULATE
NormEncoder::NormEncoder()
#include <stdio.h>
#include "protoDefs.h" // for struct timeval
#define DIFF_T(a,b) (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
NormEncoder::~NormEncoder()
{
}
NormDecoder::~NormDecoder()
{
}
NormEncoderRS8a::NormEncoderRS8a()
: npar(0), vector_size(0),
gen_poly(NULL), scratch(NULL)
{
} // end NormEncoder::NormEncoder()
} // end NormEncoderRS8a::NormEncoderRS8a()
NormEncoder::~NormEncoder()
NormEncoderRS8a::~NormEncoderRS8a()
{
if (gen_poly) Destroy();
}
bool NormEncoder::Init(int numParity, int vecSizeMax)
bool NormEncoderRS8a::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
{
// Debugging assertions
ASSERT((numParity>=0)&&(numParity<129));
ASSERT(vecSizeMax >= 0);
ASSERT((numData + numParity) <= 255);
if ((numData + numParity) > 255) return false; // (TBD) printout error message
if (gen_poly) Destroy();
npar = numParity;
@ -69,22 +81,22 @@ bool NormEncoder::Init(int numParity, int vecSizeMax)
// Create gen_poly polynomial
if(!CreateGeneratorPolynomial())
{
DMSG(0, "NormEncoder: Error creating gen_poly polynomial!\n");
DMSG(0, "NormEncoderRS8a: Error creating gen_poly 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",
DMSG(0, "NormEncoderRS8a: Error allocating memory for encoder scratch space: %s\n",
GetErrorString());
Destroy();
return false;
}
return true;
} // end NormEncoder::Init()
} // end NormEncoderRS8a::Init()
// Free memory allocated for encoder state (Encoder must be re-inited before use)
void NormEncoder::Destroy()
void NormEncoderRS8a::Destroy()
{
if(NULL != scratch)
{
@ -96,10 +108,10 @@ void NormEncoder::Destroy()
delete[] gen_poly;
gen_poly = NULL;
}
} // end NormEncoder::Destroy()
} // end NormEncoderRS8a::Destroy()
bool NormEncoder::CreateGeneratorPolynomial()
bool NormEncoderRS8a::CreateGeneratorPolynomial()
{
unsigned char *tp, *tp1, *tp2;
int degree = 2*npar;
@ -107,14 +119,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
if(!(gen_poly = new unsigned char[npar+1]))
{
DMSG(0, "NormEncoder: Error allocating memory for gen_poly polynomial: %s\n",
DMSG(0, "NormEncoderRS8a: Error allocating memory for gen_poly polynomial: %s\n",
GetErrorString());
return false;
}
/* Allocate memory for temporary polynomial arrays */
if(!(tp = new unsigned char[2*degree]))
{
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
GetErrorString());
delete[] gen_poly;
return false;
@ -123,7 +135,7 @@ bool NormEncoder::CreateGeneratorPolynomial()
{
delete[] tp;
delete[] gen_poly;
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
GetErrorString());
return false;
}
@ -132,14 +144,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
delete[] tp1;
delete[] tp;
delete[] gen_poly;
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
GetErrorString());
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++)
for (unsigned int n = 1; n <= npar; n++)
{
memset(tp, 0, degree*sizeof(unsigned char));
tp[0] = gexp(n); // set up x+a^n
@ -149,14 +161,16 @@ bool NormEncoder::CreateGeneratorPolynomial()
for (int i = 0; i < degree; i++)
{
memset(&tp2[degree], 0, degree*sizeof(unsigned char));
int j;
//unsigned int j;
// Scale tp2 by p1[i]
int j;
for(j=0; j<degree; j++) tp2[j]=gmult(tp1[j], tp[i]);
// Mult(shift) tp2 right by i
for (j = (degree*2)-1; j >= i; j--) tp2[j] = tp2[j-i];
for (j = (degree*2)-1; j >= i; j--)
tp2[j] = tp2[j-i];
memset(tp2, 0, i*sizeof(unsigned char));
// Add into partial product
for(j=0; j < (npar+1); j++) gen_poly[j] ^= tp2[j];
for(unsigned int x=0; x < (npar+1); x++) gen_poly[x] ^= tp2[x];
}
memcpy(tp1, gen_poly, (npar+1)*sizeof(unsigned char));
memset(&tp1[npar+1], 0, (2*degree)-(npar+1));
@ -165,14 +179,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
delete[] tp1;
delete[] tp;
return true;
} // end NormEncoder::CreateGeneratorPolynomial()
} // end NormEncoderRS8a::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)
void NormEncoderRS8a::Encode(const char *data, char **pVec)
{
int i, j;
unsigned char *userData, *LSFR1, *LSFR2, *pVec0;
@ -188,6 +202,7 @@ void NormEncoder::Encode(const char *data, char **pVec)
UINT16 vecSize = vector_size;
#endif // if/else SIMULATE
memcpy(scratch, pVec[0], vecSize);
if (npar > 1)
{
@ -202,36 +217,37 @@ void NormEncoder::Encode(const char *data, char **pVec)
gmult(*genPoly, (*userData++ ^ *pVec0++));
genPoly--;
}
}
pVec0 = scratch;
userData = (unsigned char *) data;
LSFR1 = (unsigned char *) pVec[npar_minus_one];
for(j = 0; j < vecSize; j++)
*LSFR1++ = gmult(*genPoly, (*userData++ ^ *pVec0++));
} // end NormEncoder::Encode()
} // end NormEncoderRS8a::Encode()
/********************************************************************************
* NormDecoder implementation routines
* NormDecoderRS8a implementation routines
*/
NormDecoder::NormDecoder()
NormDecoderRS8a::NormDecoderRS8a()
: npar(0), vector_size(0), lambda(NULL),
s_vec(NULL), o_vec(NULL), scratch(NULL)
{
}
NormDecoder::~NormDecoder()
NormDecoderRS8a::~NormDecoderRS8a()
{
if (lambda) Destroy();
if (NULL != lambda) Destroy();
}
bool NormDecoder::Init(int numParity, int vecSizeMax)
bool NormDecoderRS8a::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
{
// Debugging assertions
ASSERT((numParity>=0)&&(numParity<=128));
ASSERT(vecSizeMax >= 0);
ASSERT((numData + numParity) <= 255);
if ((numData + numParity) > 255) return false;
#ifdef SIMULATE
vecSizeMax = MIN(SIM_PAYLOAD_MAX+1, vecSizeMax);
@ -244,7 +260,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
if(!(lambda = new unsigned char[2*npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for lambda: %s\n",
DMSG(0, "NormDecoderRS8a: Error allocating memory for lambda: %s\n",
GetErrorString());
return(false);
}
@ -252,17 +268,17 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
/* Allocate memory for s_vec ptr and the syndrome vectors */
if(!(s_vec = new unsigned char*[npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for s_vec ptr: %s\n",
DMSG(0, "NormDecoderRS8a: Error allocating memory for s_vec ptr: %s\n",
GetErrorString());
Destroy();
return(false);
}
int i;
unsigned int i;
for(i=0; i < npar; i++)
{
if(!(s_vec[i] = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for new s_vec: %s\n",
DMSG(0, "NormDecoderRS8a: Error allocating memory for new s_vec: %s\n",
GetErrorString());
Destroy();
return(false);
@ -272,7 +288,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
/* Allocate memory for the o_vec ptr and the Omega vectors */
if(!(o_vec = new unsigned char*[npar]))
{
DMSG(0, "NormDecoder: Error allocating memory for new o_vec ptr: %s\n",
DMSG(0, "NormDecoderRS8a: Error allocating memory for new o_vec ptr: %s\n",
GetErrorString());
Destroy();
return(false);
@ -282,7 +298,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
{
if(!(o_vec[i] = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for new o_vec: %s",
DMSG(0, "NormDecoderRS8a: Error allocating memory for new o_vec: %s",
GetErrorString());
Destroy();
return(false);
@ -291,15 +307,15 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
if (!(scratch = new unsigned char[vecSizeMax]))
{
DMSG(0, "NormDecoder: Error allocating memory for scratch space: %s",
DMSG(0, "NormDecoderRS8a: Error allocating memory for scratch space: %s",
GetErrorString());
}
memset(scratch, 0, vecSizeMax*sizeof(unsigned char));
return(true);
} // end NormDecoder::Init()
} // end NormDecoderRS8a::Init()
void NormDecoder::Destroy()
void NormDecoderRS8a::Destroy()
{
if (scratch)
{
@ -308,14 +324,14 @@ void NormDecoder::Destroy()
}
if(o_vec)
{
for(int i=0; i<npar; i++)
for(unsigned int i=0; i<npar; i++)
if (o_vec[i]) delete[] o_vec[i];
delete[] o_vec;
o_vec = NULL;
}
if(s_vec)
{
for(int i = 0; i < npar; i++)
for(unsigned int i = 0; i < npar; i++)
if (s_vec[i]) delete[] s_vec[i];
delete[] s_vec;
s_vec = NULL;
@ -326,35 +342,35 @@ void NormDecoder::Destroy()
delete[] lambda;
lambda = NULL;
}
} // end NormDecoder::Destroy()
} // end NormDecoderRS8a::Destroy()
// This will crash & burn if (erasureCount > npar)
int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* erasureLocs)
int NormDecoderRS8a::Decode(char** dVec, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs)
{
// Debugging assertions
ASSERT(lambda);
ASSERT(erasureCount && (erasureCount<=npar));
ASSERT(erasureCount && (erasureCount <= npar));
// (A) Compute syndrome vectors
// First zero out erasure vectors (MDP provides zero-filled vecs)
// First zero out erasure vectors (NORM provides zero-filled vecs)
// Then calculate syndrome (based on zero value erasures)
int nvecs = npar + ndata;
unsigned int nvecs = npar + numData;
#ifdef SIMULATE
int vecSize = MIN(SIM_PAYLOAD_MAX+1, vector_size);
UINT16 vecSize = MIN(SIM_PAYLOAD_MAX+1, vector_size);
#else
int vecSize = vector_size;
UINT16 vecSize = vector_size;
#endif // if/else SIMUATE
int i;
unsigned int i;
for (i = 0; i < npar; i++)
{
int X = gexp(i+1);
unsigned char* synVec = s_vec[i];
memset(synVec, 0, vecSize*sizeof(char));
for(int j = 0; j < nvecs; j++)
for(unsigned int j = 0; j < nvecs; j++)
{
unsigned char* data = dVec[j] ? (unsigned char*)dVec[j] : scratch;
unsigned char* S = synVec;
@ -367,8 +383,8 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
}
// (B) Init lambda (the erasure locator polynomial)
int degree = 2*npar;
int nvecsMinusOne = nvecs - 1;
unsigned int degree = 2*npar;
unsigned int nvecsMinusOne = nvecs - 1;
memset(lambda, 0, degree*sizeof(char));
lambda[0] = 1;
for (i = 0; i < erasureCount; i++)
@ -383,13 +399,13 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
{
int k = i;
memset(o_vec[i], 0, vecSize*sizeof(char));
int m = i + 1;
for(int j = 0; j < m; j++)
//int m = i + 1;
for(unsigned int j = 0; j <= i; j++)
{
unsigned char* Omega = o_vec[i];
unsigned char* S = s_vec[j];
int Lk = lambda[k--];
for(int n = 0; n < vecSize; n++)
for(UINT16 n = 0; n < vecSize; n++)
*Omega++ ^= gmult(*S++, Lk);
}
}
@ -398,13 +414,13 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
for (i = 0; i < erasureCount; i++)
{
// Only fill _data_ erasures
if (erasureLocs[i] >= ndata) break;//return erasureCount;
if (erasureLocs[i] >= numData) break;//return erasureCount;
// evaluate lambda' (derivative) at alpha^(-i)
// ( all odd powers disappear)
int k = nvecsMinusOne - erasureLocs[i];
// (all odd powers disappear)
unsigned int k = nvecsMinusOne - erasureLocs[i];
int denom = 0;
int j;
unsigned int j;
for (j = 1; j < degree; j += 2)
denom ^= gmult(lambda[j], gexp(((255-k)*(j-1)) % 255));
// Invert for use computing error value below
@ -431,7 +447,7 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
}
}
return erasureCount;
} // end NormDecoder::Decode()
} // end NormDecoderRS8a::Decode()

View File

@ -37,50 +37,71 @@
class NormEncoder
{
// Members
private:
int npar; // No. of parity packets (n-k)
int vector_size; // Size of biggest vector to encode
unsigned char* gen_poly; // 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)(gen_poly != NULL);}
// "Encode" must be called in order of source vector0, vector1, vector2, etc
void Encode(const char *dataVector, char **parityVectorList);
int NumParity() {return npar;}
int VectorSize() {return vector_size;}
private:
bool CreateGeneratorPolynomial();
};
virtual ~NormEncoder();
virtual bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize) = 0;
virtual void Destroy() = 0;
virtual void Encode(const char *dataVector, char **parityVectorList) = 0;
}; // end class NormEncoder
class NormDecoder
{
public:
virtual ~NormDecoder();
virtual bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize) = 0;
virtual void Destroy() = 0;
virtual int Decode(char** vectorList, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs) = 0;
}; // end class NormDecoder
class NormEncoderRS8a : public NormEncoder
{
// Methods
public:
NormEncoderRS8a();
~NormEncoderRS8a();
bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize);
void Destroy();
bool IsReady(){return (bool)(gen_poly != NULL);}
// "Encode" must be called in order of source vector0, vector1, vector2, etc
void Encode(const char *dataVector, char **parityVectorList);
private:
bool CreateGeneratorPolynomial();
// Members
unsigned int npar; // No. of parity packets (n-k)
UINT16 vector_size; // Size of biggest vector to encode
unsigned char* gen_poly; // Ptr to generator polynomial
unsigned char* scratch; // scratch space for encoding
}; // end class NormEncoderRS8a
class NormDecoderRS8a : public NormDecoder
{
// Methods
public:
NormDecoderRS8a();
~NormDecoderRS8a();
bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize);
int Decode(char** vectorList, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs);
int NumParity() {return npar;}
int VectorSize() {return vector_size;}
void Destroy();
// Members
private:
int npar; // No. of parity packets (n-k)
int vector_size; // Size of biggest vector to encode
unsigned int npar; // No. of parity packets (n-k)
UINT16 vector_size; // Size of biggest vector to encode
unsigned char* lambda; // Erasure location polynomial ("2*npar" ints)
unsigned char** s_vec; // Syndrome vectors (pointers to "npar" vectors)
unsigned char** o_vec; // 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();
};
}; // end class NormDecoderRS8a

857
common/normEncoderRS8.cpp Normal file
View File

@ -0,0 +1,857 @@
/*
* This includes forward error correction code based on Vandermonde matrices
* 980624
* (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
*
* Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
* Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
* Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include "normEncoderRS8.h"
/*
* The first part of the file here implements linear algebra in GF.
*
* gf is the type used to store an element of the Galois Field.
* Must constain at least GF_BITS bits.
*
* Note: unsigned char will work up to GF(256) but int seems to run
* faster on the Pentium. We use int whenever have to deal with an
* index, since they are generally faster.
*/
#define GF_BITS 16 // 8-bit RS code
#if (GF_BITS < 2 && GF_BITS > 16)
#error "GF_BITS must be 2 .. 16"
#endif
#if (GF_BITS <= 8)
typedef UINT8 gf;
#else
typedef UINT16 gf;
#endif
#define GF_SIZE ((1 << GF_BITS) - 1) // powers of \alpha
/*
* Primitive polynomials - see Lin & Costello, Appendix A,
* and Lee & Messerschmitt, p. 453.
*/
static char *allPp[] =
{ // GF_BITS Polynomial
NULL, // 0 no code
NULL, // 1 no code
"111", // 2 1+x+x^2
"1101", // 3 1+x+x^3
"11001", // 4 1+x+x^4
"101001", // 5 1+x^2+x^5
"1100001", // 6 1+x+x^6
"10010001", // 7 1 + x^3 + x^7
"101110001", // 8 1+x^2+x^3+x^4+x^8
"1000100001", // 9 1+x^4+x^9
"10010000001", // 10 1+x^3+x^10
"101000000001", // 11 1+x^2+x^11
"1100101000001", // 12 1+x+x^4+x^6+x^12
"11011000000001", // 13 1+x+x^3+x^4+x^13
"110000100010001", // 14 1+x+x^6+x^10+x^14
"1100000000000001", // 15 1+x+x^15
"11010000000010001" // 16 1+x+x^3+x^12+x^16
};
/*
* To speed up computations, we have tables for logarithm, exponent
* and inverse of a number. If GF_BITS <= 8, we use a table for
* multiplication as well (it takes 64K, no big deal even on a PDA,
* especially because it can be pre-initialized an put into a ROM!),
* otherwhise we use a table of logarithms.
* In any case the macro gf_mul(x,y) takes care of multiplications.
*/
static gf gf_exp[2*GF_SIZE]; // index->poly form conversion table
static int gf_log[GF_SIZE + 1]; // Poly->index form conversion table
static gf inverse[GF_SIZE+1]; // inverse of field elem.
// inv[\alpha**i]=\alpha**(GF_SIZE-i-1)
// modnn(x) computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1,
// without a slow divide.
static inline gf modnn(int x)
{
while (x >= GF_SIZE)
{
x -= GF_SIZE;
x = (x >> GF_BITS) + (x & GF_SIZE);
}
return x;
} // end modnn()
#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
/*
* gf_mul(x,y) multiplies two numbers. If GF_BITS<=8, it is much
* faster to use a multiplication table.
*
* USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying
* many numbers by the same constant. In this case the first
* call sets the constant, and others perform the multiplications.
* A value related to the multiplication is held in a local variable
* declared with USE_GF_MULC . See usage in addmul1().
*/
#if (GF_BITS <= 8)
static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
#define gf_mul(x,y) gf_mul_table[x][y]
#define USE_GF_MULC register gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
static void init_mul_table()
{
int i, j;
for (i=0; i< GF_SIZE+1; i++)
for (j=0; j< GF_SIZE+1; j++)
gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ;
for (j=0; j< GF_SIZE+1; j++)
gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
}
#else /* GF_BITS > 8 */
inline gf gf_mul(int x, int y)
{
if ( (x) == 0 || (y)==0 ) return 0;
return gf_exp[gf_log[x] + gf_log[y] ] ;
}
#define init_mul_table()
#define USE_GF_MULC register gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ]
#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; }
#endif // if/else (GF_BITS <= 8)
/*
* Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
* Lookup tables:
* index->polynomial form gf_exp[] contains j= \alpha^i;
* polynomial form -> index form gf_log[ j = \alpha^i ] = i
* \alpha=x is the primitive element of GF(2^m)
*
* For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple
* multiplication of two numbers can be resolved without calling modnn
*/
//#define NEW_GF_MATRIX(rows, cols) \
// (gf *)my_malloc(rows * cols * sizeof(gf), " ## __LINE__ ## " )
#define NEW_GF_MATRIX(rows, cols) (new gf[rows*cols])
/*
* initialize the data structures used for computations in GF.
*/
static void generate_gf()
{
char *Pp = allPp[GF_BITS] ;
gf mask = 1; /* x ** 0 = 1 */
gf_exp[GF_BITS] = 0; /* will be updated at the end of the 1st loop */
/*
* first, generate the (polynomial representation of) powers of \alpha,
* which are stored in gf_exp[i] = \alpha ** i .
* At the same time build gf_log[gf_exp[i]] = i .
* The first GF_BITS powers are simply bits shifted to the left.
*/
for (int i = 0; i < GF_BITS; i++, mask <<= 1 )
{
gf_exp[i] = mask;
gf_log[gf_exp[i]] = i;
/*
* If Pp[i] == 1 then \alpha ** i occurs in poly-repr
* gf_exp[GF_BITS] = \alpha ** GF_BITS
*/
if ( Pp[i] == '1' )
gf_exp[GF_BITS] ^= mask;
}
/*
* now gf_exp[GF_BITS] = \alpha ** GF_BITS is complete, so can als
* compute its inverse.
*/
gf_log[gf_exp[GF_BITS]] = GF_BITS;
/*
* Poly-repr of \alpha ** (i+1) is given by poly-repr of
* \alpha ** i shifted left one-bit and accounting for any
* \alpha ** GF_BITS term that may occur when poly-repr of
* \alpha ** i is shifted.
*/
mask = 1 << (GF_BITS - 1 ) ;
for (int i = GF_BITS + 1; i < GF_SIZE; i++)
{
if (gf_exp[i - 1] >= mask)
gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1);
else
gf_exp[i] = gf_exp[i - 1] << 1;
gf_log[gf_exp[i]] = i;
}
/*
* log(0) is not defined, so use a special value
*/
gf_log[0] = GF_SIZE ;
/* set the extended gf_exp values for fast multiply */
for (int i = 0 ; i < GF_SIZE ; i++)
gf_exp[i + GF_SIZE] = gf_exp[i] ;
/*
* again special cases. 0 has no inverse. This used to
* be initialized to GF_SIZE, but it should make no difference
* since noone is supposed to read from here.
*/
inverse[0] = 0 ;
inverse[1] = 1;
for (int i=2; i<=GF_SIZE; i++)
inverse[i] = gf_exp[GF_SIZE-gf_log[i]];
} // end generate_gf()
/*
* Various linear algebra operations that i use often.
*/
/*
* addmul() computes dst[] = dst[] + c * src[]
* This is used often, so better optimize it! Currently the loop is
* unrolled 16 times, a good value for 486 and pentium-class machines.
* The case c=0 is also optimized, whereas c=1 is not. These
* calls are unfrequent in my typical apps so I did not bother.
*
* Note that gcc on
*/
#define addmul(dst, src, c, sz) \
if (c != 0) addmul1(dst, src, c, sz)
#define UNROLL 16 /* 1, 4, 8, 16 */
static void addmul1(gf *dst1, gf *src1, gf c, int sz)
{
USE_GF_MULC ;
register gf *dst = dst1, *src = src1 ;
gf *lim = &dst[sz - UNROLL + 1] ;
GF_MULC0(c) ;
#if (UNROLL > 1) /* unrolling by 8/16 is quite effective on the pentium */
for (; dst < lim ; dst += UNROLL, src += UNROLL )
{
GF_ADDMULC( dst[0] , src[0] );
GF_ADDMULC( dst[1] , src[1] );
GF_ADDMULC( dst[2] , src[2] );
GF_ADDMULC( dst[3] , src[3] );
#if (UNROLL > 4)
GF_ADDMULC( dst[4] , src[4] );
GF_ADDMULC( dst[5] , src[5] );
GF_ADDMULC( dst[6] , src[6] );
GF_ADDMULC( dst[7] , src[7] );
#endif
#if (UNROLL > 8)
GF_ADDMULC( dst[8] , src[8] );
GF_ADDMULC( dst[9] , src[9] );
GF_ADDMULC( dst[10] , src[10] );
GF_ADDMULC( dst[11] , src[11] );
GF_ADDMULC( dst[12] , src[12] );
GF_ADDMULC( dst[13] , src[13] );
GF_ADDMULC( dst[14] , src[14] );
GF_ADDMULC( dst[15] , src[15] );
#endif
}
#endif
lim += UNROLL - 1 ;
for (; dst < lim; dst++, src++ ) /* final components */
GF_ADDMULC( *dst , *src );
} // end addmul1()
// computes C = AB where A is n*k, B is k*m, C is n*m
static void matmul(gf *a, gf *b, gf *c, int n, int k, int m)
{
int row, col, i ;
for (row = 0; row < n ; row++)
{
for (col = 0; col < m ; col++)
{
gf *pa = &a[ row * k ];
gf *pb = &b[ col ];
gf acc = 0 ;
for (i = 0; i < k ; i++, pa++, pb += m)
acc ^= gf_mul( *pa, *pb ) ;
c[row * m + col] = acc ;
}
}
} // end matmul()
static int invert_vdm(gf *src, int k)
{
int i, j, row, col ;
gf *b, *c, *p;
gf t, xx ;
if (k == 1) /* degenerate case, matrix must be p^0 = 1 */
return 0 ;
/*
* c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
* b holds the coefficient for the matrix inversion
*/
c = NEW_GF_MATRIX(1, k);
b = NEW_GF_MATRIX(1, k);
p = NEW_GF_MATRIX(1, k);
for ( j=1, i = 0 ; i < k ; i++, j+=k )
{
c[i] = 0 ;
p[i] = src[j] ; /* p[i] */
}
/*
* construct coeffs. recursively. We know c[k] = 1 (implicit)
* and start P_0 = x - p_0, then at each stage multiply by
* x - p_i generating P_i = x P_{i-1} - p_i P_{i-1}
* After k steps we are done.
*/
c[k-1] = p[0] ; /* really -p(0), but x = -x in GF(2^m) */
for (i = 1 ; i < k ; i++)
{
gf p_i = p[i] ; /* see above comment */
for (j = k-1 - ( i - 1 ) ; j < k-1 ; j++ )
c[j] ^= gf_mul( p_i, c[j+1] ) ;
c[k-1] ^= p_i ;
}
for (row = 0 ; row < k ; row++)
{
/*
* synthetic division etc.
*/
xx = p[row] ;
t = 1 ;
b[k-1] = 1 ; /* this is in fact c[k] */
for (i = k-2 ; i >= 0 ; i-- )
{
b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ;
t = gf_mul(xx, t) ^ b[i] ;
}
for (col = 0 ; col < k ; col++ )
src[col*k + row] = gf_mul(inverse[t], b[col] );
}
delete[] c;
delete[] b;
delete[] p;
return 0 ;
} // end invert_vdm()
static bool fec_initialized = false;
static void init_fec()
{
if (!fec_initialized)
{
generate_gf();
init_mul_table();
fec_initialized = true;
}
}
NormEncoder::NormEncoder()
: enc_matrix(NULL), enc_index(0)
{
}
NormEncoder::~NormEncoder()
{
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
}
bool NormEncoder::Init(int numData, int numParity, int vectorSize)
{
if ((numData + numParity) > GF_SIZE)
{
DMSG(0, "NormEncoder::Init() error: numData/numParity exceeds code limits\n");
return false;
}
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
init_fec();
int n = numData + numParity;
int k = numData;
enc_matrix = (UINT8*)NEW_GF_MATRIX(n, k);
if (NULL != enc_matrix)
{
gf* tmpMatrix = NEW_GF_MATRIX(n, k);
if (NULL == tmpMatrix)
{
DMSG(0, "NormEncoder::Init() error: new tmpMatrix error: %s\n", GetErrorString());
delete[] enc_matrix;
enc_matrix = NULL;
return false;
}
// Fill the matrix with powers of field elements, starting from 0.
// The first row is special, cannot be computed with exp. table.
tmpMatrix[0] = 1 ;
for (int col = 1; col < k ; col++)
tmpMatrix[col] = 0 ;
for (gf* p = tmpMatrix + k, row = 0; row < n-1 ; row++, p += k)
{
for (int col = 0 ; col < k ; col ++ )
p[col] = gf_exp[modnn(row*col)];
}
// Quick code to build systematic matrix: invert the top
// k*k vandermonde matrix, multiply right the bottom n-k rows
// by the inverse, and construct the identity matrix at the top.
invert_vdm(tmpMatrix, k); /* much faster than invert_mat */
matmul(tmpMatrix + k*k, tmpMatrix, ((gf*)enc_matrix) + k*k, n - k, k, k);
// the upper matrix is I so do not bother with a slow multiply
memset(enc_matrix, 0, k*k*sizeof(gf));
for (gf* p = (gf*)enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
*p = 1 ;
delete[] tmpMatrix;
ndata = numData;
npar = numParity;
vector_size = vectorSize;
enc_index = 0;
return true;
}
else
{
DMSG(0, "NormEncoder::Init() error: new enc_matrix error: %s\n", GetErrorString());
return false;
}
} // end NormEncoder::Init()
void NormEncoder::Encode(unsigned char *dataVector, unsigned char** parityVectorList)
{
for (unsigned int i = 0; i < npar; i++)
{
// Update each parity vector
gf* fec = (gf*)parityVectorList[i];
gf* p = ((gf*)enc_matrix) + ((i+ndata)*ndata);
unsigned int nelements = (GF_BITS > 8) ? vector_size / 2 : vector_size;
addmul(fec, (gf*)dataVector, p[enc_index], nelements);
}
enc_index++;
} // end NormEncoder::Encode()
NormDecoder::NormDecoder()
: enc_matrix(NULL), dec_matrix(NULL),
parity_loc(NULL), inv_ndxc(NULL), inv_ndxr(NULL),
inv_pivt(NULL), inv_id_row(NULL), inv_temp_row(NULL)
{
}
NormDecoder::~NormDecoder()
{
Destroy();
}
void NormDecoder::Destroy()
{
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
if (NULL != dec_matrix)
{
delete[] dec_matrix;
dec_matrix = NULL;
}
if (NULL != parity_loc)
{
delete[] parity_loc;
parity_loc = NULL;
}
} // end NormDecoder::Destroy()
bool NormDecoder::Init(int numData, int numParity, int vectorSize)
{
if ((numData + numParity) > GF_SIZE)
{
DMSG(0, "NormEncoder::Init() error: numData/numParity exceeds code limits\n");
return false;
}
init_fec();
Destroy();
int n = numData + numParity;
int k = numData;
if (NULL == (inv_ndxc = new int[k]))
{
DMSG(0, "NormDecoder::Init() new inv_indxc error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_ndxr = new int[k]))
{
DMSG(0, "NormDecoder::Init() new inv_inv_ndxr error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_pivt = new int[k]))
{
DMSG(0, "NormDecoder::Init() new inv_pivt error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_id_row = (UINT8*)NEW_GF_MATRIX(1, k)))
{
DMSG(0, "NormDecoder::Init() new inv_id_row error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_temp_row = (UINT8*)NEW_GF_MATRIX(1, k)))
{
DMSG(0, "NormDecoder::Init() new inv_temp_row error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (parity_loc = new int[numParity]))
{
DMSG(0, "NormDecoder::Init() error: new parity_loc error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (dec_matrix = (UINT8*)NEW_GF_MATRIX(k, k)))
{
DMSG(0, "NormDecoder::Init() error: new dec_matrix error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (enc_matrix = (UINT8*)NEW_GF_MATRIX(n, k)))
{
DMSG(0, "NormDecoder::Init() error: new enc_matrix error: %s\n", GetErrorString());
Destroy();
return false;
}
gf* tmpMatrix = NEW_GF_MATRIX(n, k);
if (NULL == tmpMatrix)
{
DMSG(0, "NormEncoder::Init() error: new tmpMatrix error: %s\n", GetErrorString());
delete[] enc_matrix;
enc_matrix = NULL;
return false;
}
// Fill the matrix with powers of field elements, starting from 0.
// The first row is special, cannot be computed with exp. table.
tmpMatrix[0] = 1 ;
for (int col = 1; col < k ; col++)
tmpMatrix[col] = 0 ;
for (gf* p = tmpMatrix + k, row = 0; row < n-1 ; row++, p += k)
{
for (int col = 0 ; col < k ; col ++ )
p[col] = gf_exp[modnn(row*col)];
}
// Quick code to build systematic matrix: invert the top
// k*k vandermonde matrix, multiply right the bottom n-k rows
// by the inverse, and construct the identity matrix at the top.
invert_vdm(tmpMatrix, k); /* much faster than invert_mat */
matmul(tmpMatrix + k*k, tmpMatrix, ((gf*)enc_matrix) + k*k, n - k, k, k);
// the upper matrix is I so do not bother with a slow multiply
memset(enc_matrix, 0, k*k*sizeof(gf));
for (gf* p = (gf*)enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
*p = 1 ;
delete[] tmpMatrix;
ndata = numData;
npar = numParity;
vector_size = vectorSize;
return true;
} // end NormDecoder::Init()
int NormDecoder::Decode(unsigned char** vectorList, int sdata, UINT16 erasureCount, UINT16* erasureLocs)
{
int bsz = ndata + npar;
// 1) Build decoding matrix for the given set of segments & erasures
int nextErasure = 0;
int ne = 0;
int sourceErasureCount = 0;
int parityCount = 0;
for (int i = 0; i < bsz; i++)
{
if (i < sdata)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
sourceErasureCount++;
}
else
{
// set identity row for segments we have
gf* p = ((gf*)dec_matrix) + ndata*i;
memset(p, 0, ndata*sizeof(gf));
p[i] = 1;
}
}
else if (i < ndata)
{
// set identity row for assumed zero segments (shortened code)
gf* p = ((gf*)dec_matrix) + ndata*i;
memset(p, 0, ndata*sizeof(gf));
p[i] = 1;
// Also keep track of where the non-erased parity segment are
// for the shortened code
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
}
else if (parityCount < sourceErasureCount)
{
parity_loc[parityCount++] = i;
// Copy appropriate enc_matric parity row to dec_matrix erasureRow
gf* p = ((gf*)dec_matrix) + ndata*erasureLocs[ne++];
memcpy(p, ((gf*)enc_matrix) + (ndata-sdata+i)*ndata, ndata*sizeof(gf));
}
}
else if (parityCount < sourceErasureCount)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
}
else
{
ASSERT(parityCount < npar);
parity_loc[parityCount++] = i;
// Copy appropriate enc_matric parity row to dec_matrix erasureRow
gf* p = ((gf*)dec_matrix) + ndata*erasureLocs[ne++];
memcpy(p, ((gf*)enc_matrix) + (ndata-sdata+i)*ndata, ndata*sizeof(gf));
}
}
else
{
break;
}
}
ASSERT(ne == sourceErasureCount);
// 2) Invert the decoding matrix
if (!InvertDecodingMatrix())
{
DMSG(0, "NormDecoder::Decode() error: couldn't invert dec_matrix ?!\n");
return 0;
}
// 3) Decode
for (int e = 0; e < erasureCount; e++)
{
// Calculate missing segments (erasures) using dec_matrix and non-erasures
int row = erasureLocs[e];
int col = 0;
int nextErasure = 0;
int nelements = (GF_BITS > 8) ? vector_size/2 : vector_size;
for (int i = 0; i < sdata; i++)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
// Use parity segments in place of erased vector in decoding
addmul((gf*)vectorList[row], (gf*)vectorList[parity_loc[nextErasure]], ((gf*)dec_matrix)[row*ndata + col], nelements);
col++;
nextErasure++; // point to next erasure
}
else if (i < sdata)
{
addmul((gf*)vectorList[row], (gf*)vectorList[i], ((gf*)dec_matrix)[row*ndata + col], nelements);
col++;
}
else
{
ASSERT(0);
}
}
}
return erasureCount ;
} // end NormDecoder::Decode()
/*
* NormDecoder::InvertDecodingMatrix() takes a matrix and produces its inverse
* k is the size of the matrix. (Gauss-Jordan, adapted from Numerical Recipes in C)
* Return non-zero if singular.
*/
bool NormDecoder::InvertDecodingMatrix()
{
gf* src = (gf*)dec_matrix;
int k = ndata;
memset(inv_id_row, 0, k*sizeof(gf));
// inv_pivt marks elements already used as pivots.
memset(inv_pivt, 0, k*sizeof(gf));
for (int col = 0; col < k ; col++)
{
/*
* Zeroing column 'col', look for a non-zero element.
* First try on the diagonal, if it fails, look elsewhere.
*/
int irow = -1;
int icol = -1 ;
if (inv_pivt[col] != 1 && src[col*k + col] != 0)
{
irow = col ;
icol = col ;
goto found_piv ;
}
for (int row = 0 ; row < k ; row++)
{
if (inv_pivt[row] != 1)
{
for (int ix = 0 ; ix < k ; ix++)
{
if (inv_pivt[ix] == 0)
{
if (src[row*k + ix] != 0)
{
irow = row ;
icol = ix ;
goto found_piv ;
}
}
else if (inv_pivt[ix] > 1)
{
DMSG(0, "NormDecoder::InvertDecodingMatrix() error: singular matrix!\n");
return false;
}
}
}
}
if (icol == -1)
{
DMSG(0, "NormDecoder::InvertDecodingMatrix() error: pivot not found!\n");
return false;
}
found_piv:
++(inv_pivt[icol]) ;
/*
* swap rows irow and icol, so afterwards the diagonal
* element will be correct. Rarely done, not worth
* optimizing.
*/
if (irow != icol)
{
for (int ix = 0 ; ix < k ; ix++ )
SWAP(src[irow*k + ix], src[icol*k + ix], gf);
}
inv_ndxr[col] = irow ;
inv_ndxc[col] = icol ;
gf* pivotRow = &src[icol*k] ;
gf c = pivotRow[icol] ;
if (c == 0)
{
DMSG(0, "NormDecoder::InvertDecodingMatrix() error: singular matrix!\n");
return false;
}
if (c != 1 ) /* otherwhise this is a NOP */
{
/*
* this is done often , but optimizing is not so
* fruitful, at least in the obvious ways (unrolling)
*/
c = inverse[ c ] ;
pivotRow[icol] = 1 ;
for (int ix = 0 ; ix < k ; ix++ )
pivotRow[ix] = gf_mul(c, pivotRow[ix] );
}
/*
* from all rows, remove multiples of the selected row
* to zero the relevant entry (in fact, the entry is not zero
* because we know it must be zero).
* (Here, if we know that the pivot_row is the identity,
* we can optimize the addmul).
*/
inv_id_row[icol] = 1;
if (0 != memcmp(pivotRow, inv_id_row, k*sizeof(gf)))
{
for (gf* p = src, ix = 0 ; ix < k ; ix++, p += k )
{
if (ix != icol)
{
c = p[icol] ;
p[icol] = 0 ;
addmul(p, pivotRow, c, k );
}
}
}
inv_id_row[icol] = 0;
} // end for (col = 0; col < k ; col++)
for (int col = k-1 ; col >= 0 ; col-- )
{
if (inv_ndxr[col] <0 || inv_ndxr[col] >= k)
{
DMSG(0, "NormDecoder::InvertDecodingMatrix() error: AARGH, inv_ndxr[col] %d\n", inv_ndxr[col]);
}
else if (inv_ndxc[col] <0 || inv_ndxc[col] >= k)
{
DMSG(0, "NormDecoder::InvertDecodingMatrix() error: AARGH, indxc[col] %d\n", inv_ndxc[col]);
}
else if (inv_ndxr[col] != inv_ndxc[col] )
{
for (int row = 0 ; row < k ; row++ )
SWAP( src[row*k + inv_ndxr[col]], src[row*k + inv_ndxc[col]], gf) ;
}
}
return true;
} // end NormDecoder::InvertDecodingMatrix()

70
common/normEncoderRS8.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef _NORM_ENCODER_RS8
#define _NORM_ENCODER_RS8
#include "protoDebug.h"
#include "protoDefs.h" // for UINT16
// (TBD) We're going to need to have a "NormEncoder" base class and then allow for variants
class NormEncoder
{
// Methods
public:
NormEncoder();
~NormEncoder();
bool Init(int numData, int numParity, int vectorSize);
void Destroy();
// "Encode" must be called in order of source vector0, vector1, vector2, etc
void Encode(unsigned char *dataVector, unsigned char **parityVectorList);
int GetNumData() {return ndata;}
int GetNumParity() {return npar;}
int GetVectorSize() {return vector_size;}
// Members
private:
int ndata; // max data pkts per block (k)
int npar; // No. of parity packets (n-k)
int vector_size; // Size of biggest vector to encode
unsigned char* enc_matrix;
int enc_index;
}; // end class NormEncoder
class NormDecoder
{
public:
NormDecoder();
~NormDecoder();
bool Init(int numData, int numParity, int vectorSize);
// Note: "erasureLocs" must be in order from lowest to highest!
int Decode(unsigned char** vectorList, int ndata, UINT16 erasureCount, UINT16* erasureLocs);
int NumParity() {return npar;}
int VectorSize() {return vector_size;}
void Destroy();
// Members
private:
bool InvertDecodingMatrix(); // used in Decode() method
int ndata; // max data pkts per block (k)
int npar; // No. of parity packets (n-k)
int vector_size; // Size of biggest vector to encode
unsigned char* enc_matrix;
unsigned char* dec_matrix;
int* parity_loc;
// These "inv_" members are used in InvertDecodingMatrix()
int* inv_ndxc;
int* inv_ndxr;
int* inv_pivt;
unsigned char* inv_id_row;
unsigned char* inv_temp_row;
}; // end class NormDecoder
#endif // _NORM_ENCODER_RS8

View File

@ -4,6 +4,7 @@
#include <stdio.h> // for rename()
#ifdef WIN32
#ifndef _WIN32_WCE
#include <errno.h>
#include <direct.h>
#include <share.h>
#include <io.h>
@ -47,7 +48,9 @@ bool NormFile::Open(const char* thePath, int theFlags)
char tempPath[PATH_MAX];
strncpy(tempPath, thePath, PATH_MAX);
char* ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
if (ptr) *ptr = '\0';
if (NULL != ptr)
{
*ptr = '\0';
ptr = NULL;
while (!NormFile::Exists(tempPath))
{
@ -64,6 +67,7 @@ bool NormFile::Open(const char* thePath, int theFlags)
break;
}
}
}
if (ptr && ('\0' == *ptr)) *ptr++ = PROTO_PATH_DELIMITER;
while (ptr)
{
@ -162,7 +166,6 @@ bool NormFile::Lock()
{
#ifndef WIN32 // WIN32 files are automatically locked
fchmod(fd, 0640 | S_ISGID);
#ifdef HAVE_FLOCK
if (flock(fd, LOCK_EX | LOCK_NB))
return false;
@ -334,50 +337,70 @@ bool NormFile::Rename(const char* oldName, const char* newName)
size_t NormFile::Read(char* buffer, size_t len)
{
ASSERT(IsOpen());
size_t got = 0;
while (got < len)
{
#ifdef WIN32
#ifdef _WIN32_WCE
size_t result = fread(buffer, 1, len, file_ptr);
size_t result = fread(buffer+got, 1, len-got, file_ptr);
#else
size_t result = _read(fd, buffer, (unsigned int)len);
size_t result = _read(fd, buffer+got, (unsigned int)(len-got));
#endif // if/else _WIN32_WCE
#else
ssize_t result = read(fd, buffer, len);
ssize_t result = read(fd, buffer+got, len-got);
#endif // if/else WIN32
if (result < 0)
if (result <= 0)
{
DMSG(0, "NormFile::Read() read(%d) error: %s (fd = %d, offset:%d)\n", len, GetErrorString(), fd, offset);
#ifndef _WIN32_WCE
if (EINTR != errno)
#endif // !_WIN32_WCE
{
DMSG(0, "NormFile::Read() read(%d) result:%d error:%s (offset:%d)\n", len, result, GetErrorString(), offset);
return 0;
}
}
else
{
got += result;
offset += (Offset)result;
return result;
}
} // end while (have < want)
return got;
} // end NormFile::Read()
size_t NormFile::Write(const char* buffer, size_t len)
{
ASSERT(IsOpen());
size_t put = 0;
while (put < len)
{
#ifdef WIN32
#ifdef _WIN32_WCE
size_t result = fwrite(buffer, 1, len, file_ptr);
size_t result = fwrite(buffer+put, 1, len-put, file_ptr);
#else
size_t result = _write(fd, buffer, (unsigned int)len);
size_t result = _write(fd, buffer+put, (unsigned int)(len-put));
#endif // if/else _WIN32_WCE
#else
size_t result = write(fd, buffer, len);
size_t result = write(fd, buffer+put, len-put);
#endif // if/else WIN32
if (result < 0)
if (result <= 0)
{
DMSG(0, "NormFile::Write() write() error: %s\n", GetErrorString());
#ifndef _WIN32_WCE
if (EINTR != errno)
#endif // !_WIN32_WCE
{
DMSG(0, "NormFile::Write() write(%d) result:%d error: %s\n", len, result, GetErrorString());
return 0;
}
}
else
{
offset += (Offset)result;
return result;
put += result;
}
return result;
} // end while (put < len)
return put;
} // end NormFile::Write()
bool NormFile::Seek(Offset theOffset)
@ -395,7 +418,7 @@ bool NormFile::Seek(Offset theOffset)
#endif // if/else WIN32
if (result == (Offset)-1)
{
DMSG(0, "NormFile::Seek() lseek() error: %s", GetErrorString());
DMSG(0, "NormFile::Seek() lseek() error: %s\n", GetErrorString());
return false;
}
else
@ -405,6 +428,28 @@ bool NormFile::Seek(Offset theOffset)
}
} // end NormFile::Seek()
bool NormFile::Pad(Offset theOffset)
{
if (theOffset > GetSize())
{
if (Seek(theOffset - 1))
{
char byte = 0;
if (1 != Write(&byte, 1))
{
DMSG(0, "NormFile::Pad() write error: %s\n", GetErrorString());
return false;
}
}
else
{
DMSG(0, "NormFile::Pad() seek error: %s\n", GetErrorString());
return false;
}
}
return true;
} // end NormFile::Pad()
NormFile::Offset NormFile::GetSize() const
{
ASSERT(IsOpen());

View File

@ -11,14 +11,14 @@
#else
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif // if/else WIN32
#ifdef _WIN32_WCE
#include <stdio.h>
#else
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif // if/else _WIN32_WCE
// From PROTOLIB
@ -56,7 +56,7 @@ class NormFile
bool Lock();
void Unlock();
bool Rename(const char* oldName, const char* newName);
bool Unlink(const char *path);
static bool Unlink(const char *path);
void Close();
bool IsOpen() const
{
@ -71,6 +71,7 @@ class NormFile
bool Seek(Offset theOffset);
NormFile::Offset GetOffset() const {return (offset);}
NormFile::Offset GetSize() const;
bool Pad(Offset theOffset); // if file size is less than theOffset, writes a byte to force filesize
// static helper methods
static NormFile::Type GetType(const char *path);

View File

@ -431,3 +431,118 @@ unsigned char NormQuantizeRtt(double rtt)
return ((unsigned char)(ceil(255.0 - (13.0*log(NORM_RTT_MAX/rtt)))));
} // end NormQuantizeRtt()
// These are lookup tables to support for efficient NORM field
// conversion for those fields that are quantized values
////////////////////////////////////////////////
// The following table provides a lookup of NORM
// RTT approximation from the 8-bit "rtt" and
// "grtt" fields of some NORM messages. The
// following routine was used to generate
// this table:
//
// inline double NormUnquantizeRtt(unsigned char qrtt)
// {
// return ((qrtt < 31) ?
// (((double)(qrtt+1))*(double)NORM_RTT_MIN) :
// (NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
// }
//
const double NORM_RTT[256] =
{
1.000e-06, 2.000e-06, 3.000e-06, 4.000e-06,
5.000e-06, 6.000e-06, 7.000e-06, 8.000e-06,
9.000e-06, 1.000e-05, 1.100e-05, 1.200e-05,
1.300e-05, 1.400e-05, 1.500e-05, 1.600e-05,
1.700e-05, 1.800e-05, 1.900e-05, 2.000e-05,
2.100e-05, 2.200e-05, 2.300e-05, 2.400e-05,
2.500e-05, 2.600e-05, 2.700e-05, 2.800e-05,
2.900e-05, 3.000e-05, 3.100e-05, 3.287e-05,
3.550e-05, 3.833e-05, 4.140e-05, 4.471e-05,
4.828e-05, 5.215e-05, 5.631e-05, 6.082e-05,
6.568e-05, 7.093e-05, 7.660e-05, 8.273e-05,
8.934e-05, 9.649e-05, 1.042e-04, 1.125e-04,
1.215e-04, 1.313e-04, 1.417e-04, 1.531e-04,
1.653e-04, 1.785e-04, 1.928e-04, 2.082e-04,
2.249e-04, 2.429e-04, 2.623e-04, 2.833e-04,
3.059e-04, 3.304e-04, 3.568e-04, 3.853e-04,
4.161e-04, 4.494e-04, 4.853e-04, 5.241e-04,
5.660e-04, 6.113e-04, 6.602e-04, 7.130e-04,
7.700e-04, 8.315e-04, 8.980e-04, 9.698e-04,
1.047e-03, 1.131e-03, 1.222e-03, 1.319e-03,
1.425e-03, 1.539e-03, 1.662e-03, 1.795e-03,
1.938e-03, 2.093e-03, 2.260e-03, 2.441e-03,
2.636e-03, 2.847e-03, 3.075e-03, 3.321e-03,
3.586e-03, 3.873e-03, 4.182e-03, 4.517e-03,
4.878e-03, 5.268e-03, 5.689e-03, 6.144e-03,
6.635e-03, 7.166e-03, 7.739e-03, 8.358e-03,
9.026e-03, 9.748e-03, 1.053e-02, 1.137e-02,
1.228e-02, 1.326e-02, 1.432e-02, 1.547e-02,
1.670e-02, 1.804e-02, 1.948e-02, 2.104e-02,
2.272e-02, 2.454e-02, 2.650e-02, 2.862e-02,
3.090e-02, 3.338e-02, 3.604e-02, 3.893e-02,
4.204e-02, 4.540e-02, 4.903e-02, 5.295e-02,
5.718e-02, 6.176e-02, 6.669e-02, 7.203e-02,
7.779e-02, 8.401e-02, 9.072e-02, 9.798e-02,
1.058e-01, 1.143e-01, 1.234e-01, 1.333e-01,
1.439e-01, 1.554e-01, 1.679e-01, 1.813e-01,
1.958e-01, 2.114e-01, 2.284e-01, 2.466e-01,
2.663e-01, 2.876e-01, 3.106e-01, 3.355e-01,
3.623e-01, 3.913e-01, 4.225e-01, 4.563e-01,
4.928e-01, 5.322e-01, 5.748e-01, 6.207e-01,
6.704e-01, 7.240e-01, 7.819e-01, 8.444e-01,
9.119e-01, 9.848e-01, 1.064e+00, 1.149e+00,
1.240e+00, 1.340e+00, 1.447e+00, 1.562e+00,
1.687e+00, 1.822e+00, 1.968e+00, 2.125e+00,
2.295e+00, 2.479e+00, 2.677e+00, 2.891e+00,
3.122e+00, 3.372e+00, 3.641e+00, 3.933e+00,
4.247e+00, 4.587e+00, 4.953e+00, 5.349e+00,
5.777e+00, 6.239e+00, 6.738e+00, 7.277e+00,
7.859e+00, 8.487e+00, 9.166e+00, 9.898e+00,
1.069e+01, 1.154e+01, 1.247e+01, 1.346e+01,
1.454e+01, 1.570e+01, 1.696e+01, 1.832e+01,
1.978e+01, 2.136e+01, 2.307e+01, 2.491e+01,
2.691e+01, 2.906e+01, 3.138e+01, 3.389e+01,
3.660e+01, 3.953e+01, 4.269e+01, 4.610e+01,
4.979e+01, 5.377e+01, 5.807e+01, 6.271e+01,
6.772e+01, 7.314e+01, 7.899e+01, 8.530e+01,
9.212e+01, 9.949e+01, 1.074e+02, 1.160e+02,
1.253e+02, 1.353e+02, 1.462e+02, 1.578e+02,
1.705e+02, 1.841e+02, 1.988e+02, 2.147e+02,
2.319e+02, 2.504e+02, 2.704e+02, 2.921e+02,
3.154e+02, 3.406e+02, 3.679e+02, 3.973e+02,
4.291e+02, 4.634e+02, 5.004e+02, 5.404e+02,
5.836e+02, 6.303e+02, 6.807e+02, 7.351e+02,
7.939e+02, 8.574e+02, 9.260e+02, 1.000e+03
};
////////////////////////////////////////////////
// The following tables is provides a lookup
// of NORM group size approximation from the
// 4-bit "gsize" field of some NORM messages
// The following routine was used to generate
// this table
//
// inline double NormUnquantizeGroupSize(unsigned char gsize)
// {
// double exponent = (double)((gsize & 0x07) + 1);
// double mantissa = (0 != (gsize & 0x08)) ? 5.0 : 1.0;
// return (mantissa * pow(10.0, exponent));
// }
//
const double NORM_GSIZE[16] =
{
1.000e+01, 1.000e+02, 1.000e+03, 1.000e+04,
1.000e+05, 1.000e+06, 1.000e+07, 1.000e+08,
5.000e+01, 5.000e+02, 5.000e+03, 5.000e+04,
5.000e+05, 5.000e+06, 5.000e+07, 5.000e+08
};

View File

@ -51,20 +51,20 @@ const double NORM_GRTT_MIN = 0.001; // 1 msec
const double NORM_GRTT_MAX = 15.0; // 15 sec
const double NORM_RTT_MIN = 1.0e-06;
const double NORM_RTT_MAX = 1000.0;
extern const double NORM_RTT[];
inline double NormUnquantizeRtt(unsigned char qrtt)
{
return ((qrtt < 31) ?
(((double)(qrtt+1))*(double)NORM_RTT_MIN) :
(NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
return NORM_RTT[qrtt];
}
unsigned char NormQuantizeRtt(double rtt);
extern const double NORM_GSIZE[];
inline double NormUnquantizeGroupSize(unsigned char gsize)
{
double exponent = (double)((gsize & 0x07) + 1);
double mantissa = (0 != (gsize & 0x08)) ? 5.0 : 1.0;
return (mantissa * pow(10.0, exponent));
return NORM_GSIZE[gsize];
}
inline unsigned char NormQuantizeGroupSize(double gsize)
{
unsigned char ebits = (unsigned char)log10(gsize);
@ -1386,6 +1386,7 @@ class NormCmdApplicationMsg : public NormCmdMsg
class NormNackMsg : public NormMsg
{
public:
enum {DEFAULT_LENGTH_MAX = 40};
void Init()
{
SetType(NACK);

View File

@ -40,14 +40,13 @@ NormCCNode::~NormCCNode()
{
}
const double NormServerNode::DEFAULT_NOMINAL_INTERVAL = 2*NormSession::DEFAULT_GRTT_ESTIMATE;
const double NormServerNode::ACTIVITY_INTERVAL_MIN = 1.0; // 1 second min activity timeout
NormServerNode::NormServerNode(class NormSession& theSession, NormNodeId nodeId)
: NormNode(theSession, nodeId), instance_id(0), synchronized(false), sync_id(0),
max_pending_range(256), is_open(false), segment_size(0), ndata(0), nparity(0),
repair_boundary(BLOCK_BOUNDARY), erasure_loc(NULL),
repair_boundary(BLOCK_BOUNDARY), decoder(NULL), erasure_loc(NULL),
retrieval_loc(NULL), retrieval_pool(NULL),
cc_sequence(0), cc_enable(false), cc_rate(0.0),
rtt_confirmed(false), is_clr(false), is_plr(false),
@ -228,20 +227,28 @@ bool NormServerNode::AllocateBuffers(UINT16 segmentSize,
}
retrieval_index = 0;
if (!(retrieval_loc = new UINT16[numData]))
if (!(retrieval_loc = new unsigned int[numData]))
{
DMSG(0, "NormServerNode::AllocateBuffers() retrieval_loc allocation error: %s\n", GetErrorString());
Close();
return false;
}
if (!decoder.Init(numParity, segmentSize+NormDataMsg::GetStreamPayloadHeaderLength()))
if (NULL != decoder) delete decoder;
if (NULL == (decoder = new NormDecoderRS8a))
{
DMSG(0, "NormServerNode::AllocateBuffers() new NormDecoderRSa error: %s\n", GetErrorString());
Close();
return false;
}
if (!decoder->Init(numData, numParity, segmentSize+NormDataMsg::GetStreamPayloadHeaderLength()))
{
DMSG(0, "NormServerNode::AllocateBuffers() decoder init error\n");
Close();
return false;
}
if (!(erasure_loc = new UINT16[numParity]))
if (!(erasure_loc = new unsigned int[numParity]))
{
DMSG(0, "NormServerNode::AllocateBuffers() erasure_loc allocation error: %s\n", GetErrorString());
Close();
@ -262,7 +269,12 @@ void NormServerNode::FreeBuffers()
delete[] erasure_loc;
erasure_loc = NULL;
}
decoder.Destroy();
if (NULL != decoder)
{
decoder->Destroy();
delete decoder;
decoder = NULL;
}
if (retrieval_loc)
{
delete[] retrieval_loc;
@ -270,7 +282,7 @@ void NormServerNode::FreeBuffers()
}
if (retrieval_pool)
{
for (UINT16 i = 0; i < ndata; i++)
for (unsigned int i = 0; i < ndata; i++)
{
if (retrieval_pool[i])
{
@ -285,10 +297,9 @@ void NormServerNode::FreeBuffers()
NormObject* obj;
while ((obj = rx_table.Find(rx_table.RangeLo())))
{
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
UINT16 objectId = obj->GetId();
DeleteObject(obj);
// We do the following to remember which objects were pending
AbortObject(obj);
// We do the following to remember which _objects_ were pending
rx_pending_mask.Set(objectId);
}
@ -304,6 +315,7 @@ void NormServerNode::UpdateGrttEstimate(UINT8 grttQuantized)
DMSG(4, "NormServerNode::UpdateGrttEstimate() node>%lu server>%lu new grtt: %lf sec\n",
LocalNodeId(), GetId(), grtt_estimate);
// activity timer depends upon sender's grtt estimate
// (TBD) do a proper rescaling here instead?
double activityInterval = 2*NORM_ROBUST_FACTOR*grtt_estimate;
if (activityInterval < ACTIVITY_INTERVAL_MIN) activityInterval = ACTIVITY_INTERVAL_MIN;
activity_timer.SetInterval(activityInterval);
@ -361,10 +373,12 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
cc.GetSendTime(grtt_send_time);
cc_sequence = cc.GetCCSequence();
NormCCRateExtension ext;
bool hasCCRateExtension = false;
while (cc.GetNextExtension(ext))
{
if (NormHeaderExtension::CC_RATE == ext.GetType())
{
hasCCRateExtension = true;
cc_enable = true;
send_rate = NormUnquantizeRate(ext.GetSendRate());
// Are we in the cc_node_list?
@ -444,6 +458,8 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
session.ActivateTimer(cc_timer);
} // end if (CC_RATE == ext.GetType())
} // end while (GetNextExtension())
// Disable CC feedback if sender doesn't want it
if (!hasCCRateExtension && cc_enable) cc_enable = false;
break;
}
case NormCmdMsg::FLUSH:
@ -468,7 +484,9 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
if (doAck)
{
// Force sync since we're expected to ACK
// and request repair for object indicated
Sync(flush.GetObjectId());
SetPending(flush.GetObjectId());
}
else
{
@ -1032,12 +1050,24 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (NULL != obj)
{
rx_table.Insert(obj);
if (129 == msg.GetFecId())
{
// Look for FEC Object Transport Information header extension
NormFtiExtension fti;
while (msg.GetNextExtension(fti))
{
if (NormHeaderExtension::FTI == fti.GetType())
{
// Pre-open receive object and notify app for accept.
#ifndef ASSUME_MDP_FEC
if (0 != fti.GetFecInstanceId())
{
DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC instance id\n");
DeleteObject(obj);
obj = NULL;
}
else
#endif // !ASSUME_MDP_FEC
if (obj->Open(fti.GetObjectSize(),
msg.FlagIsSet(NormObjectMsg::FLAG_INFO),
fti.GetSegmentSize(),
@ -1069,7 +1099,7 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
break;
}
}
if (obj && !obj->IsOpen())
if ((NULL != obj) && !obj->IsOpen())
{
DMSG(0, "NormServerNode::HandleObjectMessage() node>%lu server>%lu "
"new obj>%hu - no FTI provided!\n", LocalNodeId(), GetId(), (UINT16)objectId);
@ -1077,6 +1107,13 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
obj = NULL;
}
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC type\n");
DeleteObject(obj);
obj = NULL;
}
}
break;
}
case OBJ_COMPLETE:
@ -1090,7 +1127,14 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (obj)
{
obj->HandleObjectMessage(msg, msgType, blockId, segmentId);
if (!obj->IsPending())
bool objIsPending = obj->IsPending();
// Silent receivers may be configured to allow obj completion w/out INFO
if (objIsPending && session.RcvrIgnoreInfo())
objIsPending = obj->PendingMaskIsSet();
if (!objIsPending)
{
// Reliable reception of this object has completed
if (NormObject::FILE == obj->GetType())
@ -1102,7 +1146,7 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (NormObject::STREAM != obj->GetType())
{
// Streams never complete unless they are "closed" by sender
// and this handled within stream control code in "normObject.cpp"
// and this is handled within stream control code in "normObject.cpp"
session.Notify(NormController::RX_OBJECT_COMPLETED, this, obj);
DeleteObject(obj);
obj = NULL;
@ -1145,6 +1189,21 @@ bool NormServerNode::SyncTest(const NormObjectMsg& msg) const
} // end NormServerNode::SyncTest()
// a little helper method
void NormServerNode::AbortObject(NormObject* obj)
{
// it it's a file, close it first, so app can do something
if (NormObject::FILE == obj->GetType())
#ifdef SIMULATE
static_cast<NormSimObject*>(obj)->Close();
#else
static_cast<NormFileObject*>(obj)->Close();
#endif // !SIMULATE
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
DeleteObject(obj);
failure_count++;
} // end NormServerNode::AbortObject()
// This method establishes the sync point "sync_id"
// objectId. The sync point is the first ordinal
@ -1176,9 +1235,7 @@ void NormServerNode::Sync(NormObjectId objectId)
NormObject* obj;
while ((obj = rx_table.Find(rx_table.RangeLo())))
{
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
DeleteObject(obj);
failure_count++;
AbortObject(obj);
}
rx_pending_mask.Clear();
}
@ -1188,9 +1245,7 @@ void NormServerNode::Sync(NormObjectId objectId)
while ((obj = rx_table.Find(rx_table.RangeLo())) &&
(obj->GetId() < objectId))
{
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
DeleteObject(obj);
failure_count++;
AbortObject(obj);
}
unsigned long numBits = (UINT16)(objectId - firstPending) + 1;
rx_pending_mask.UnsetBits(firstPending, numBits);
@ -1495,7 +1550,10 @@ bool NormServerNode::OnRepairTimeout(ProtoTimer& /*theTimer*/)
{
//if (slow_start) // (TBD) should we only set flag on actual slow_start?
ext.SetCCFlag(NormCC::START);
if (recv_rate > 0.0)
ext.SetCCRate(NormQuantizeRate(2.0 * recv_rate));
else
ext.SetCCRate(NormQuantizeRate(2.0 * nominal_packet_size)); // (TBD revisit this
}
else
{
@ -1568,7 +1626,10 @@ bool NormServerNode::OnRepairTimeout(ProtoTimer& /*theTimer*/)
}
if (NormRepairRequest::INVALID != nextForm)
{
if (0 != segment_size)
nack->AttachRepairRequest(req, segment_size); // (TBD) error check
else
nack->AttachRepairRequest(req, NormNackMsg::DEFAULT_LENGTH_MAX);
req.SetForm(nextForm);
req.ResetFlags();
// Set flags for missing objects according to
@ -2413,7 +2474,7 @@ NormLossEstimator2::NormLossEstimator2()
event_window(0), event_index(0),
event_window_time(0.0), event_index_time(0.0),
seeking_loss_event(true),
no_loss(true), initial_loss(0.0), loss_interval(0.0),
no_loss(true), initial_loss(0.0),
current_discount(1.0)
{
memset(history, 0, 9*sizeof(unsigned long));
@ -2527,11 +2588,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
if (seeking_loss_event)
{
double scale;
if (history[0] > loss_interval)
scale = 0.125 / (1.0 + log((double)(event_window ? event_window : 1)));
else
scale = 0.125;
if (outageDepth) // non-zero outageDepth means pkt loss(es)
{
if (no_loss) // first loss
@ -2547,11 +2603,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
no_loss = false;
}
// Old method
if (loss_interval > 0.0)
loss_interval += scale*(((double)history[0]) - loss_interval);
else
loss_interval = (double) history[0];
// New method
// New loss event, shift loss interval history & discounts
@ -2571,19 +2622,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
(((double)currentTime.tv_usec)/1.0e06));
event_index_time += event_window_time;
}
else
{
//if (no_loss) fprintf(stderr, "No loss (seq:%u) ...\n", theSequence);
if (loss_interval > 0.0)
{
double diff = ((double)history[0]) - loss_interval;
if (diff >= 1.0)
{
//scale *= (diff * diff) / (loss_interval * loss_interval);
loss_interval += scale*log(diff);
}
}
}
}
else
{
@ -2595,20 +2633,9 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
return newLossEvent;
} // end NormLossEstimator2::Update()
double NormLossEstimator2::LossFraction()
{
#if defined0
if (use_ewma_loss_estimate)
return MdpLossFraction(); // MDP EWMA approach
else
#endif // SIMULATOR
return (TfrcLossFraction()); // ACIRI TFRC approach
} // end NormLossEstimator2::LossFraction()
// TFRC Loss interval averaging with discounted, weighted averaging
double NormLossEstimator2::TfrcLossFraction()
double NormLossEstimator2::LossFraction()
{
if (!history[1]) return 0.0;
// Compute older weighted average s1->s8 for discount determination

View File

@ -91,9 +91,7 @@ class NormLossEstimator2
unsigned short seqNumber,
bool ecn = false);
double LossFraction();
double MdpLossFraction()
{return ((loss_interval > 0.0) ? (1.0/loss_interval) : 0.0);}
double TfrcLossFraction();
bool NoLoss() {return no_loss;}
void SetInitialLoss(double lossFraction)
{
@ -121,8 +119,6 @@ class NormLossEstimator2
bool no_loss;
double initial_loss;
double loss_interval; // EWMA of loss event interval
unsigned long history[9]; // loss interval history
double discount[9];
double current_discount;
@ -296,6 +292,8 @@ class NormServerNode : public NormNode
}
void SetPending(NormObjectId objectId);
void AbortObject(NormObject* obj);
void DeleteObject(NormObject* obj);
UINT16 SegmentSize() {return segment_size;}
@ -340,7 +338,7 @@ class NormServerNode : public NormNode
UINT16 Decode(char** segmentList, UINT16 numData, UINT16 erasureCount)
{
return decoder.Decode(segmentList, numData, erasureCount, erasure_loc);
return decoder->Decode(segmentList, numData, erasureCount, erasure_loc);
}
void CalculateGrttResponse(const struct timeval& currentTime,
@ -397,8 +395,8 @@ class NormServerNode : public NormNode
bool is_open;
UINT16 segment_size;
UINT16 ndata;
UINT16 nparity;
unsigned int ndata;
unsigned int nparity;
NormObjectTable rx_table;
ProtoSlidingMask rx_pending_mask;
@ -408,11 +406,11 @@ class NormServerNode : public NormNode
bool unicast_nacks;
NormBlockPool block_pool;
NormSegmentPool segment_pool;
NormDecoder decoder;
UINT16* erasure_loc;
UINT16* retrieval_loc;
NormDecoder* decoder;
unsigned int* erasure_loc;
unsigned int* retrieval_loc;
char** retrieval_pool;
UINT16 retrieval_index;
unsigned int retrieval_index;
bool server_active;
ProtoTimer activity_timer;

View File

@ -131,16 +131,16 @@ bool NormObject::Open(const NormObjectSize& objectSize,
}
// Init pending_mask (everything pending)
if (!pending_mask.Init(numBlocks.LSB(), 0xffffffff))
if (!pending_mask.Init(numBlocks.LSB(), (UINT32)0xffffffff))
{
DMSG(0, "NormObject::Open() init pending_mask (%lu) error: %s\n",
numBlocks.GetOffset(), GetErrorString());
numBlocks.LSB(), GetErrorString());
Close();
return false;
}
// Init repair_mask
if (!repair_mask.Init(numBlocks.LSB(), 0xffffffff))
if (!repair_mask.Init(numBlocks.LSB(), (UINT32)0xffffffff))
{
DMSG(0, "NormObject::Open() init pending_mask error\n");
Close();
@ -367,7 +367,7 @@ bool NormObject::ActivateRepairs()
GetLastRepair(lastId); // for debug output only
DMSG(6, "NormObject::ActivateRepairs() node>%lu obj>%hu activating blk>%lu->%lu block repairs ...\n",
LocalNodeId(), (UINT16)transport_id, (UINT32)nextId, (UINT32)lastId);
repairsActivated = true;
//repairsActivated = true;
UINT16 autoParity = session.ServerAutoParity();
do
{
@ -375,7 +375,9 @@ bool NormObject::ActivateRepairs()
if (block) block->TxReset(GetBlockSize(nextId), nparity, autoParity, segment_size);
// (TBD) This check can be eventually eliminated if everything else is done right
if (!pending_mask.Set(nextId))
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error!\n", (UINT32)nextId);
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error 1!\n", (UINT32)nextId);
else
repairsActivated = true;
nextId++;
} while (GetNextRepair(nextId));
repair_mask.Clear();
@ -388,12 +390,14 @@ bool NormObject::ActivateRepairs()
{
if (block->ActivateRepairs(nparity))
{
repairsActivated = true;
//repairsActivated = true;
DMSG(6, "NormObject::ActivateRepairs() node>%lu obj>%hu activated blk>%lu segment repairs ...\n",
LocalNodeId(), (UINT16)transport_id, (UINT32)block->GetId());
// (TBD) This check can be eventually eliminated if everything else is done right
if (!pending_mask.Set(block->GetId()))
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error!\n", (UINT32)block->GetId());
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error 2!\n", (UINT32)block->GetId());
else
repairsActivated = true;
}
}
return repairsActivated;
@ -441,8 +445,6 @@ bool NormObject::AppendRepairAdv(NormCmdRepairAdvMsg& cmd)
{
NormBlockId currentId = nextId;
nextId++;
//DMSG(0, "NormObject::AppendRepairAdv() testing block:%lu nextId:%lu endId:%lu\n",
// (UINT32)currentId, (UINT32)nextId, (UINT32)endId);
bool repairEntireBlock = repair_mask.Test(currentId);
if (repairEntireBlock)
{
@ -673,7 +675,7 @@ bool NormObject::PassiveRepairCheck(NormBlockId blockId,
NormSegmentId firstPendingSegment;
if (block->GetFirstPending(firstPendingSegment))
{
if (segmentId > firstPendingSegment)
if (firstPendingSegment <= segmentId)
return true;
else
return false;
@ -700,11 +702,13 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
{
// (TBD) make sure it's OK for "max_pending_segment"
// and "next_segment_id" to be > blockSize
bool thruObject = false;
switch (level)
{
case TO_OBJECT:
return false;
case THRU_INFO:
// (TBD) set "thruObject" if info-only obj
break;
case TO_BLOCK:
if (blockId >= max_pending_block)
@ -712,6 +716,7 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
max_pending_block = blockId;
max_pending_segment = 0;
}
// (TBD) set "thruObject" if info-only obj
break;
case THRU_SEGMENT:
if (blockId > max_pending_block)
@ -724,6 +729,12 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
if (segmentId >= max_pending_segment)
max_pending_segment = segmentId + 1;
}
if (blockId == final_block_id)
{
unsigned int finalSegment = GetBlockSize(blockId) - 1;
if (finalSegment <= segmentId)
thruObject = true;
}
break;
case THRU_BLOCK:
if (blockId > max_pending_block)
@ -731,6 +742,8 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
max_pending_block = blockId;
max_pending_segment = GetBlockSize(blockId);
}
if (blockId == final_block_id)
thruObject = true;
break;
case THRU_OBJECT:
if (!IsStream())
@ -738,11 +751,19 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
else if (blockId > max_pending_block)
max_pending_block = blockId;
max_pending_segment = GetBlockSize(max_pending_block);
thruObject = true;
break;
default:
break;
} // end switch (level)
if (thruObject && session.RcvrIsLowDelay())
{
ASSERT(NULL != server);
server->AbortObject(this);
return false;
}
if (timerActive)
{
if (holdoffPhase) return false;
@ -801,9 +822,9 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
if (level > TO_BLOCK) lastId++;
while (nextId < lastId)
{
if (nextId > lastId) break;
if (nextId > lastId) break; // (TBD) is this line unecessary?
NormBlock* block = block_buffer.Find(nextId);
if (block)
if (NULL != block)
{
if ((nextId == blockId) && (THRU_SEGMENT == level))
{
@ -854,6 +875,7 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
else
current_block_id = final_block_id;
next_segment_id = GetBlockSize(current_block_id);
break;
default:
break;
}
@ -1047,7 +1069,6 @@ bool NormObject::AppendRepairRequest(NormNackMsg& nack,
} // end if (appendRequest)
nextId++;
iterating = GetNextPending(nextId);
//DMSG(0, "got next pending>%lu result:%d\n", (UINT32)nextId, iterating);
iterating = iterating && (flush || (nextId <= max_pending_block));
} // end while (iterating || (0 != consecutiveCount))
@ -1119,17 +1140,14 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
UINT16 numData = GetBlockSize(blockId);
// For stream objects, a little extra mgmt is required
NormStreamObject* stream = NULL;
if (STREAM == type)
{
NormStreamObject* stream = static_cast<NormStreamObject*>(this);
stream = static_cast<NormStreamObject*>(this);
if (!stream->StreamUpdateStatus(blockId))
{
DMSG(4, "NormObject::HandleObjectMessage() node:%lu server:%lu obj>%hu blk>%lu "
"broken stream ...\n", LocalNodeId(), server->GetId(), (UINT16)transport_id, (UINT32)blockId);
//ASSERT(0);
// ??? Ignore this new packet and try to fix stream ???
//return;
server->IncrementResyncCount();
while (!stream->StreamUpdateStatus(blockId))
{
@ -1214,22 +1232,6 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
if (isSourceSymbol)
{
block->DecrementErasureCount();
/* STREAM payload flags have been deprecated in favor of "payload_msg_start"
// This bit of code makes sure that NORM Protocol Version 1
// use of FLAG_MSG_START is observed even when the equivalent (and better!)
// stream payload flag is not used.
if (IsStream())
{
if (data.FlagIsSet(NormObjectMsg::FLAG_MSG_START) &&
!NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(),
NormDataMsg::FLAG_MSG_START))
{
DMSG(0, "NormObject::HandleObjectMessage() setting missing FLAG_MSG_START\n");
NormDataMsg::SetStreamPayloadFlag((char*)data.GetPayload(), NormDataMsg::FLAG_MSG_START);
}
}
*/
if (WriteSegment(blockId, segmentId, data.GetPayload()))
{
objectUpdated = true;
@ -1238,7 +1240,10 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
}
else
{
if (IsStream())
DMSG(4, "NormObject::HandleObjectMessage() WriteSegment() error\n");
else
DMSG(0, "NormObject::HandleObjectMessage() WriteSegment() error\n");
}
}
else
@ -1297,11 +1302,11 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
}
}
nextErasure = numData;
// Set erasure locs for any missing parity symbol segments
// Set erasure locs for any missing parity symbol segments, too!
while (block->GetNextPending(nextErasure))
server->SetErasureLoc(erasureCount++, nextErasure++);
} // end if (nextErasure < numData)
} // end (block->GetFirstPending(nextErasure))
} // end if (block->GetFirstPending(nextErasure))
if (erasureCount)
{
@ -1315,8 +1320,16 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
{
objectUpdated = true;
// For statistics only (TBD) #ifdef NORM_DEBUG
// "segmentLength" is not necessarily correct here (TBD - fix this)
server->IncrementRecvGoodput(segmentLength);
}
else
{
if (IsStream())
DMSG(4, "NormObject::HandleObjectMessage() WriteSegment() error\n");
else
DMSG(0, "NormObject::HandleObjectMessage() WriteSegment() error\n");
}
}
else
{
@ -1336,11 +1349,14 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
// (TBD) this could be improved for stream objects
// so it's not called unnecessarily
if (objectUpdated && notify_on_update)
{
if ((NULL == stream) || stream->IsReadReady())
{
notify_on_update = false;
session.Notify(NormController::RX_OBJECT_UPDATED, server, this);
}
}
}
else
{
DMSG(6, "NormObject::HandleObjectMessage() node>%lu server>%lu obj>%hu "
@ -1494,6 +1510,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
fti.SetSegmentSize(segment_size);
fti.SetObjectSize(object_size);
fti.SetFecInstanceId(0); // ZERO is for legacy MDP/NORM FEC encoder (TBD - use appropriate instanceId)
fti.SetFecMaxBlockLen(ndata);
fti.SetFecNumParity(nparity);
@ -1510,18 +1527,31 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
{
// Attempt to advance stream (probably was repair-delayed)
if (IsStream())
{
static_cast<NormStreamObject*>(this)->StreamAdvance();
if (pending_mask.IsSet())
{
return NextServerMsg(msg);
}
else
DMSG(0, "NormObject::NextServerMsg() pending object w/ no pending blocks?\n");
{
ASSERT(IsRepairPending());
return false;
}
}
else
{
DMSG(0, "NormObject::NextServerMsg() pending object w/ no pending blocks?!\n");
return false;
}
}
NormDataMsg* data = (NormDataMsg*)msg;
UINT16 numData = GetBlockSize(blockId);
NormBlock* block = block_buffer.Find(blockId);
if (!block)
{
if (!(block = session.ServerGetFreeBlock(transport_id, blockId)))
if (NULL == (block = session.ServerGetFreeBlock(transport_id, blockId)))
{
DMSG(2, "NormObject::NextServerMsg() node>%lu warning: server resource "
"constrained (no free blocks).\n", LocalNodeId());
@ -1558,44 +1588,42 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
{
NormBlock* b = block_buffer.Find(block_buffer.RangeLo());
ASSERT(NULL != b);
//ASSERT(!b->IsPending()); (TBD) ??? Why might "b" be pending?
bool push = static_cast<NormStreamObject*>(this)->GetPushMode();
if (!push && (b->IsRepairPending() || IsRepairSet(b->GetId())))
{
// Pending repairs delaying stream advance
DMSG(0, "NormObject::NextServerMsg() node>%lu pending repairs delaying stream progress\n", LocalNodeId());
DMSG(4, "NormObject::NextServerMsg() node>%lu pending repairs delaying stream progress\n", LocalNodeId());
session.ServerPutFreeBlock(block);
return false;
}
else
{
// Prune old non-pending block (or even pending if "push" enabled stream)
block_buffer.Remove(b);
if (IsStream())
static_cast<NormStreamObject*>(this)->UnlockBlock(b->GetId());
session.ServerPutFreeBlock(b);
continue;
}
}
else if (IsStream())
{
DMSG(0, "NormObject::NextServerMsg() node>%lu Warning! can't repair old block\n", LocalNodeId());
DMSG(4, "NormObject::NextServerMsg() node>%lu Warning! can't repair old block\n", LocalNodeId());
session.ServerPutFreeBlock(block);
pending_mask.Unset(blockId);
if (pending_mask.IsSet())
{
// Unlock (set to non-pending status) the corresponding stream_buffer block
static_cast<NormStreamObject*>(this)->UnlockBlock(blockId);
return NextServerMsg(msg);
}
else
{
static_cast<NormStreamObject*>(this)->StreamAdvance();
return false;
}
}
else
{
ASSERT(0);
DMSG(0, "NormObject::NextServerMsg() invalid non-stream state!\n");
return false;
}
}
}
} // end if (!block)
NormSegmentId segmentId = 0;
if (!block->GetFirstPending(segmentId))
{
@ -1620,7 +1648,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
data->SetPayloadLength(payloadLength);
// Perform incremental FEC encoding as needed
if ((block->ParityReadiness() == segmentId) && nparity)
if ((block->ParityReadiness() == segmentId) && (0 != nparity))
// (TBD) && ((incrementalParity == true) || (auto_parity != 0))
{
// (TBD) for non-stream objects, catch alternate "last block/segment len"
@ -1659,8 +1687,12 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
data->SetFecSymbolId(segmentId);
if (!block->IsPending())
{
// End of block reached
block->ResetParityCount(nparity);
pending_mask.Unset(blockId);
// for EMCON sending, mark NORM_INFO for re-transmission, if applicable
if (session.SndrEmcon() && HaveInfo())
pending_info = true;
}
if (!pending_mask.IsSet())
@ -1682,8 +1714,10 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
return true;
} // end NormObject::NextServerMsg()
void NormStreamObject::StreamAdvance()
{
// (TBD) should we make sure !pending_mask.IsSet()???
NormBlockId nextBlockId = stream_next_id;
// Make sure we won't prevent any pending repairs
if (repair_mask.CanSet(nextBlockId))
@ -1693,7 +1727,8 @@ void NormStreamObject::StreamAdvance()
if (pending_mask.Set(nextBlockId))
stream_next_id++;
else
DMSG(0, "NormStreamObject::StreamAdvance() error setting stream pending mask (1)\n");
DMSG(0, "NormStreamObject::StreamAdvance() error: node>%lu couldn't set set stream pending mask (1)\n",
LocalNodeId());
}
else
{
@ -1706,18 +1741,20 @@ void NormStreamObject::StreamAdvance()
if (pending_mask.Set(nextBlockId))
stream_next_id++;
else
DMSG(0, "NormStreamObject::StreamAdvance() error setting stream pending mask (2)\n");
DMSG(0, "NormStreamObject::StreamAdvance() error: node>%lu couldn't set stream pending mask (2)\n",
LocalNodeId());
}
else
{
DMSG(0, "NormStreamObject::StreamAdvance() warning: node>%lu Pending segment repairs (blk>%lu) "
DMSG(4, "NormStreamObject::StreamAdvance() warning: node>%lu pending segment repairs (blk>%lu) "
"delaying stream advance ...\n", LocalNodeId(), (UINT32)block->GetId());
}
}
}
else
{
DMSG(0, "NormStreamObject::StreamAdvance() pending block repair delaying stream advance ...\n");
DMSG(4, "NormStreamObject::StreamAdvance() warning: node>%lu pending block repair delaying stream advance ...\n",
LocalNodeId());
}
} // end NormStreamObject::StreamAdvance()
@ -1914,6 +1951,8 @@ bool NormFileObject::Accept(const char* thePath)
void NormFileObject::Close()
{
NormObject::Close();
if (NULL != server) // we've been receiving this file
file.Unlock();
file.Close();
} // end NormFileObject::Close()
@ -2277,9 +2316,8 @@ NormStreamObject::NormStreamObject(class NormSession& theSession,
class NormServerNode* theServer,
const NormObjectId& objectId)
: NormObject(STREAM, theSession, theServer, objectId),
stream_sync(false), sync_offset_valid(false),
write_vacancy(false), posted_tx_queue_vacancy(false),
read_init(true),
stream_sync(false), write_vacancy(false),
read_init(true), read_ready(false),
flush_pending(false), msg_start(true),
flush_mode(FLUSH_NONE), push_mode(false),
stream_closing(false),
@ -2362,7 +2400,6 @@ bool NormStreamObject::Open(UINT32 bufferSize,
tx_index.block = tx_index.segment = 0;
tx_offset = write_offset = read_offset = 0;
write_vacancy = true;
posted_tx_queue_vacancy = false;
if (!server)
{
@ -2381,7 +2418,6 @@ bool NormStreamObject::Open(UINT32 bufferSize,
}
stream_sync = false;
sync_offset_valid = false;
flush_pending = false;
msg_start = true;
stream_closing = false;
@ -2445,6 +2481,12 @@ bool NormStreamObject::LockBlocks(NormBlockId firstId, NormBlockId lastId)
return true;
} // end NormStreamObject::LockBlocks()
void NormStreamObject::UnlockBlock(NormBlockId blockId)
{
NormBlock* block = stream_buffer.Find(blockId);
if (NULL != block) block->ClearPending();
} // end NormStreamObject::UnlockBlock()
bool NormStreamObject::LockSegments(NormBlockId blockId, NormSegmentId firstId, NormSegmentId lastId)
{
@ -2495,7 +2537,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
else
{
// Stream broken
//DMSG(0, "NormObject::StreamUpdateStatus() broken 1 ...\n");
DMSG(4, "NormObject::StreamUpdateStatus() broken 1 ...\n");
return false;
}
}
@ -2505,7 +2547,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
if (delta > NormBlockId(pending_mask.GetSize()))
{
// Stream broken
//DMSG(0, "NormObject::StreamUpdateStatus() broken 2 ...\n");
DMSG(4, "NormObject::StreamUpdateStatus() broken 2 ...\n");
return false;
}
else
@ -2538,7 +2580,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
stream_sync_id = blockId;
stream_next_id = blockId + pending_mask.GetSize();
// Since we're doing a resync including "read_init", dump any buffered data
// (TBD) this may not be necessary and is thus currently commented-out code
// (TBD) this may not be necessary??? and is thus currently commented-out code
/*read_init = true;
NormBlock* block;
while (NULL != (block = stream_buffer.Find(stream_buffer.RangeLo())))
@ -2578,6 +2620,7 @@ UINT16 NormStreamObject::ReadSegment(NormBlockId blockId,
//DMSG(0, "NormStreamObject::ReadSegment() stream starved (1)\n");
return false;
}
// (TBD) should we check to see if "blockId > write_index.block" ?
if ((blockId == write_index.block) &&
(segmentId >= write_index.segment))
{
@ -2624,11 +2667,6 @@ UINT16 NormStreamObject::ReadSegment(NormBlockId blockId,
}
}
}
/*if (HasVacancy() && !posted_tx_queue_vacancy)
{
posted_tx_queue_vacancy = true;
session.Notify(NormController::TX_QUEUE_VACANCY, NULL, this);
} */
UINT16 segmentLength = NormDataMsg::ReadStreamPayloadLength(segment);
ASSERT(segmentLength <= segment_size);
@ -2649,13 +2687,13 @@ bool NormStreamObject::WriteSegment(NormBlockId blockId,
const char* segment)
{
UINT32 segmentOffset = NormDataMsg::ReadStreamPayloadOffset(segment);
if (read_init)
{
read_init = false;
read_index.block = blockId;
read_index.segment = segmentId;
read_offset = segmentOffset;
read_ready = true;
}
if ((blockId < read_index.block) ||
@ -2779,13 +2817,29 @@ bool NormStreamObject::WriteSegment(NormBlockId blockId,
memcpy(s, segment, payloadLength);
block->AttachSegment(segmentId, s);
block->SetPending(segmentId);
ASSERT(block->GetSegment(segmentId) == s);
}
if (!sync_offset_valid)
if (!read_ready)
{
// Set "sync_offset" on first received data buffered.
sync_offset = segmentOffset;
sync_offset_valid = true;
// Did this segment make our stream ready for reading
if ((blockId == read_index.block) && (segmentId == read_index.segment))
{
read_ready = true;
}
else if (block_pool.GetCount() < block_pool_threshold)
{
// We're starting to fill up receive stream buffer
// with alot of partially received coding blocks
read_ready = true;
}
else if (session.RcvrIsLowDelay())
{
INT32 delta = blockId - read_index.block;
if (delta > session.RcvrGetMaxDelay())
read_ready = true;
}
}
}
return true;
} // end NormStreamObject::WriteSegment()
@ -2845,8 +2899,18 @@ void NormStreamObject::Prune(NormBlockId blockId, bool updateStatus)
}
} // end NormStreamObject::Prune()
// Sequential (in order) read/write routines (TBD) Add a "Seek()" method
bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStart)
{
bool result = ReadPrivate(buffer, buflen, seekMsgStart);
if (!read_ready)
SetNotifyOnUpdate(true); // reset notification when stream is no longer "read_ready"
return result;
} // end NormStreamObject::Read()
// Sequential (in order) read/write routines (TBD) Add a "Seek()" method
bool NormStreamObject::ReadPrivate(char* buffer, unsigned int* buflen, bool seekMsgStart)
{
if (stream_closing || read_init)
{
@ -2856,7 +2920,6 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
return seekMsgStart ? false : true;
}
Retain();
SetNotifyOnUpdate(true); // reset notification when streams are read
unsigned int bytesRead = 0;
unsigned int bytesToRead = *buflen;
do
@ -2865,6 +2928,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
if (!block)
{
//DMSG(0, "NormStreamObject::Read() stream buffer empty (1) (sbEmpty:%d)\n", stream_buffer.IsEmpty());
read_ready = false;
*buflen = bytesRead;
if (bytesRead > 0)
{
@ -2873,8 +2937,21 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
}
else
{
if ((block_pool.GetCount() < block_pool_threshold) ||
(session.ReceiverIsLowDelay() && (read_index.block < max_pending_block)))
bool forceForward = false;
if (block_pool.GetCount() < block_pool_threshold)
{
// We're starting to fill up receive stream buffer
// with alot of partially received coding blocks
forceForward = true;
}
else if (session.RcvrIsLowDelay())
{
// Has the sender moved forward to the next FEC blocks
long delta = max_pending_block - read_index.block;
if (delta > session.RcvrGetMaxDelay())
forceForward = true;
}
if (forceForward)
{
// Force read_index forward and try again.
if (++read_index.segment >= ndata)
@ -2900,6 +2977,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
{
//DMSG(0, "NormStreamObject::Read(%lu:%hu) stream buffer empty (2)\n",
// (UINT32)read_index.block, read_index.segment);
read_ready = false;
*buflen = bytesRead;
if (bytesRead > 0)
{
@ -2908,8 +2986,21 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
}
else
{
if ((block_pool.GetCount() < block_pool_threshold) ||
(session.ReceiverIsLowDelay() && (read_index.block < max_pending_block)))
bool forceForward = false;
if (block_pool.GetCount() < block_pool_threshold)
{
// We're starting to fill up receive stream buffer
// with alot of partially received coding blocks
forceForward = true;
}
else if (session.RcvrIsLowDelay())
{
// Has the sender moved forward to the next FEC blocks
long delta = max_pending_block - read_index.block;
if (delta > session.RcvrGetMaxDelay())
forceForward = true;
}
if (forceForward)
{
// Force read_index forward and try again.
if (++read_index.segment >= ndata)
@ -2930,7 +3021,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
}
}
} // end if (!segment)
read_ready = true;
UINT16 length = NormDataMsg::ReadStreamPayloadLength(segment);
if (0 == length)
{
@ -2958,6 +3049,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
}
else if (length > segment_size)
{
// This segment is an invalid segment because its length is too long
Release();
if (bytesRead > 0)
{
@ -2980,6 +3072,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
Prune(read_index.block, false);
}
*buflen = 0;
read_ready = false; //DetermineReadReadiness();
return false;
}
}
@ -3002,6 +3095,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
LocalNodeId(), (UINT16)transport_id, (UINT32)read_index.block, read_index.segment, read_offset, segmentOffset);
read_offset = segmentOffset;
*buflen = 0;
read_ready = false; //DetermineReadReadiness();
return false;
}
}
@ -3025,16 +3119,13 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
// Reset our read_offset ...
read_offset = segmentOffset;
*buflen = 0;
read_ready = false; //DetermineReadReadiness();
return false;
}
}
if (seekMsgStart)
{
/* stream payload flags have in deprecated in favor of "payload_msg_start"
if ((0 != index) ||
!NormDataMsg::StreamPayloadFlagIsSet(segment, NormDataMsg::FLAG_MSG_START))
*/
UINT16 msgStart = NormDataMsg::ReadStreamPayloadMsgStart(segment);
if (0 == msgStart)
{
@ -3101,7 +3192,15 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
read_index.block++;
read_index.segment = 0;
Prune(read_index.block, false);
if (0 == bytesToRead)
read_ready = DetermineReadReadiness();
}
else
{
if (0 == bytesToRead)
read_ready = (NULL != block->GetSegment(read_index.segment));
}
if (streamEnded)
{
DMSG(4, "NormStreamObject::Read() stream ended by sender 2\n");
@ -3194,8 +3293,6 @@ void NormStreamObject::Terminate()
}
NormDataMsg::WriteStreamPayloadOffset(segment, write_offset);
//NormDataMsg::ResetStreamPayloadFlags(segment); // flags are deprecated!
//NormDataMsg::SetStreamPayloadFlag(segment, NormDataMsg::FLAG_STREAM_END);
block->SetPending(write_index.segment);
if (++write_index.segment >= ndata)
{
@ -3231,7 +3328,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
if (deltaBlock > (block_pool.GetTotal() >> 1))
{
write_vacancy = false;
DMSG(8, "NormStreamObject::Write() stream buffer full (1)\n", len, eom);
DMSG(4, "NormStreamObject::Write() stream buffer full (1)\n");
if (!push_mode) break;
}
NormBlock* block = stream_buffer.Find(write_index.block);
@ -3263,7 +3360,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
}
else
{
DMSG(8, "NormStreamObject::Write() stream buffer full (2) len:%d eom:%d\n", len, eom);
DMSG(4, "NormStreamObject::Write() stream buffer full (2) len:%d eom:%d\n", len, eom);
break;
}
}
@ -3304,7 +3401,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
}
else
{
DMSG(8, "NormStreamObject::Write() stream buffer full (3)\n");
DMSG(4, "NormStreamObject::Write() stream buffer full (3)\n");
break;
}
}
@ -3372,11 +3469,9 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
}
else
{
if (0 != nBytes)
//if (0 != nBytes)
session.TouchServer();
}
if ((0 != nBytes) && posted_tx_queue_vacancy)
posted_tx_queue_vacancy = false;
return nBytes;
} // end NormStreamObject::Write()
@ -3473,6 +3568,33 @@ bool NormObjectTable::Init(UINT16 rangeMax, UINT16 tableSize)
return true;
} // end NormObjectTable::Init()
void NormObjectTable::SetRangeMax(UINT16 rangeMax)
{
if (rangeMax < range_max)
{
// Prune if necessary
while (range > rangeMax)
{
NormObject* obj = Find(range_lo);
ASSERT(obj);
NormServerNode* sender = obj->GetServer();
NormSession& session = obj->GetSession();
if (NULL == sender)
{
session.Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, obj);
session.DeleteTxObject(obj);
}
else
{
if (!session.ClientIsSilent()) obj = Find(range_hi);
session.Notify(NormController::RX_OBJECT_ABORTED, sender, obj);
sender->DeleteObject(obj);
}
}
}
range_max = rangeMax;
} // end NormObjectTable::SetRangeMax()
void NormObjectTable::Destroy()
{
if (table)

View File

@ -44,7 +44,9 @@ class NormObject
// This must be reset after each update
void SetNotifyOnUpdate(bool state)
{notify_on_update = state;}
{
notify_on_update = state;
}
// Object information
NormObject::Type GetType() const {return type;}
@ -116,6 +118,8 @@ class NormObject
bool IsPending(bool flush = true) const;
bool IsRepairPending() const;
bool IsPendingInfo() {return pending_info;}
bool PendingMaskIsSet() const
{return pending_mask.IsSet();}
bool GetFirstPending(NormBlockId& blockId) const
{
UINT32 index = 0;
@ -297,6 +301,8 @@ class NormFileObject : public NormObject
result ? strncpy(path, newPath, PATH_MAX) : NULL;
return result;
}
bool PadToSize()
{return file.Pad(GetSize().GetOffset());}
virtual bool WriteSegment(NormBlockId blockId,
NormSegmentId segmentId,
@ -404,8 +410,6 @@ class NormStreamObject : public NormObject
bool Read(char* buffer, unsigned int* buflen, bool findMsgStart = false);
UINT32 Write(const char* buffer, UINT32 len, bool eom = false);
bool SyncOffsetIsValid() {return sync_offset_valid;}
UINT32 GetSyncOffset() {return sync_offset;}
UINT32 GetCurrentReadOffset() {return read_offset;}
bool StreamUpdateStatus(NormBlockId blockId);
@ -434,8 +438,9 @@ class NormStreamObject : public NormObject
void Rewind();
bool LockBlocks(NormBlockId nextId, NormBlockId lastId);
bool LockSegments(NormBlockId blockId, NormSegmentId firstId,
NormSegmentId lastId);
void UnlockBlock(NormBlockId blockId);
bool LockSegments(NormBlockId blockId, NormSegmentId firstId, NormSegmentId lastId);
NormBlockId StreamBufferLo() {return stream_buffer.RangeLo();}
void Prune(NormBlockId blockId, bool updateStatus);
@ -456,12 +461,19 @@ class NormStreamObject : public NormObject
void SetBlockPoolThreshold(UINT32 value)
{block_pool_threshold = value;}
void ResetPostedTxQueueVacancy() {posted_tx_queue_vacancy = false;}
bool IsReadReady() const {return read_ready;}
bool DetermineReadReadiness()
{
NormBlock* block = stream_buffer.Find(read_index.block);
return ((NULL != block) && (NULL != block->GetSegment(read_index.segment)));
}
bool IsReadIndex(NormBlockId blockId, NormSegmentId segmentId) const
{return ((blockId == read_index.block) && (segmentId == read_index.segment));}
private:
bool ReadPrivate(char* buffer, unsigned int* buflen, bool findMsgStart = false);
void Terminate();
class Index
@ -474,8 +486,6 @@ class NormStreamObject : public NormObject
bool stream_sync;
NormBlockId stream_sync_id;
NormBlockId stream_next_id;
UINT32 sync_offset;
bool sync_offset_valid;
NormBlockPool block_pool;
NormSegmentPool segment_pool;
@ -485,10 +495,10 @@ class NormStreamObject : public NormObject
Index tx_index;
UINT32 tx_offset;
bool write_vacancy;
bool posted_tx_queue_vacancy;
bool read_init;
Index read_index;
UINT32 read_offset;
bool read_ready;
bool flush_pending;
bool msg_start;
FlushMode flush_mode;
@ -539,9 +549,11 @@ class NormObjectTable
NormObjectTable();
~NormObjectTable();
bool Init(UINT16 rangeMax, UINT16 tableSize = 256);
void SetRangeMax(UINT16 rangeMax);
void Destroy();
bool IsInited() const {return (NULL != table);}
UINT16 GetRangeMax() const {return range_max;}
bool CanInsert(NormObjectId objectId) const;
bool Insert(NormObject* theObject);
bool Remove(NormObject* theObject);

1211
common/normPrecode.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -491,8 +491,7 @@ NormObjectSize NormBlock::GetBytesPending(UINT16 numData,
} while (GetNextPending(nextId));
}
// Correct for final_segment_size, if applicable
if ((id == finalBlockId) &&
(IsPending(numData - 1)))
if ((id == finalBlockId) && IsPending(numData - 1))
{
pendingBytes -= NormObjectSize(segmentSize);
pendingBytes += NormObjectSize(finalSegmentSize);

View File

@ -19,10 +19,6 @@ class NormSegmentPool
void Put(char* segment)
{
ASSERT(seg_count < seg_total);
//char** ptr = (char**)segment;
//ptr = seg_list;
// (TBD) avoid this system call
//memcpy(segment, &seg_list, sizeof(char*));
*((char**)segment) = seg_list; // this might make a warning on Solaris
seg_list = segment;
seg_count++;
@ -32,7 +28,6 @@ class NormSegmentPool
unsigned long CurrentUsage() const {return (seg_total - seg_count);}
unsigned long PeakUsage() const {return peak_usage;}
unsigned long OverunCount() const {return overruns;}
unsigned int GetSegmentSize() {return seg_size;}
private:

View File

@ -13,16 +13,21 @@ const double NormSession::DEFAULT_BACKOFF_FACTOR = 4.0;
const double NormSession::DEFAULT_GSIZE_ESTIMATE = 1000.0;
const UINT16 NormSession::DEFAULT_NDATA = 64;
const UINT16 NormSession::DEFAULT_NPARITY = 32;
const UINT16 NormSession::DEFAULT_TX_CACHE_MIN = 8;
const UINT16 NormSession::DEFAULT_TX_CACHE_MAX = 256;
NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
: session_mgr(sessionMgr), notify_pending(false), tx_port(0),
tx_socket(&tx_socket_actual), tx_socket_actual(ProtoSocket::UDP),
tx_socket_actual(ProtoSocket::UDP), tx_socket(&tx_socket_actual),
rx_socket(ProtoSocket::UDP), local_node_id(localNodeId),
ttl(DEFAULT_TTL), tos(0), loopback(false), rx_port_reuse(false), rx_addr_bind(false),
tx_rate(DEFAULT_TRANSMIT_RATE/8.0), tx_rate_min(-1.0), tx_rate_max(-1.0),
backoff_factor(DEFAULT_BACKOFF_FACTOR), is_server(false), instance_id(0),
ndata(DEFAULT_NDATA), nparity(DEFAULT_NPARITY), auto_parity(0), extra_parity(0),
next_tx_object_id(0), tx_cache_count_min(8), tx_cache_count_max(256),
sndr_emcon(false), encoder(NULL),
next_tx_object_id(0),
tx_cache_count_min(DEFAULT_TX_CACHE_MIN),
tx_cache_count_max(DEFAULT_TX_CACHE_MAX),
tx_cache_size_max((UINT32)20*1024*1024),
flush_count(NORM_ROBUST_FACTOR+1),
posted_tx_queue_empty(false),
@ -36,7 +41,8 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY),
grtt_response(false), grtt_current_peak(0.0), grtt_age(0.0),
cc_enable(false), cc_sequence(0), cc_slow_start(true), cc_active(false),
is_client(false), unicast_nacks(false), client_silent(false), receiver_low_delay(false),
is_client(false), unicast_nacks(false),
client_silent(false), rcvr_ignore_info(false), rcvr_max_delay(-1),
default_repair_boundary(NormServerNode::BLOCK_BOUNDARY),
default_nacking_mode(NormObject::NACK_NORMAL),
trace(false), tx_loss_rate(0.0), rx_loss_rate(0.0),
@ -82,6 +88,7 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
report_timer.SetRepeat(-1);
}
NormSession::~NormSession()
{
Close();
@ -229,9 +236,8 @@ void NormSession::Close()
bool NormSession::SetMulticastInterface(const char* interfaceName)
{
if (interfaceName)
if (NULL != interfaceName)
{
bool result = true;
if (rx_socket.IsOpen())
result &= rx_socket.SetMulticastInterface(interfaceName);
@ -358,19 +364,19 @@ bool NormSession::StartServer(UINT16 instanceId,
if (!Open(interfaceName)) return false;
}
// (TBD) parameterize the object history depth
if (!tx_table.Init(256))
if (!tx_table.Init(tx_cache_count_max))
{
DMSG(0, "NormSession::StartServer() tx_table.Init() error!\n");
StopServer();
return false;
}
if (!tx_pending_mask.Init(256, 0x0000ffff))
if (!tx_pending_mask.Init(tx_cache_count_max, 0x0000ffff))
{
DMSG(0, "NormSession::StartServer() tx_pending_mask.Init() error!\n");
StopServer();
return false;
}
if (!tx_repair_mask.Init(256, 0x0000ffff))
if (!tx_repair_mask.Init(tx_cache_count_max, 0x0000ffff))
{
DMSG(0, "NormSession::StartServer() tx_repair_mask.Init() error!\n");
StopServer();
@ -407,7 +413,15 @@ bool NormSession::StartServer(UINT16 instanceId,
if (numParity)
{
if (!encoder.Init(numParity, segmentSize + NormDataMsg::GetStreamPayloadHeaderLength()))
if (NULL != encoder) delete encoder;
if (NULL == (encoder = new NormEncoderRS8a))
{
DMSG(0, "NormSession::StartServer() new NormEncoderRS8a error: %s\n", GetErrorString());
StopServer();
return false;
}
if (!encoder->Init(numData, numParity, segmentSize + NormDataMsg::GetStreamPayloadHeaderLength()))
{
DMSG(0, "NormSession::StartServer() encoder init error\n");
StopServer();
@ -477,7 +491,12 @@ void NormSession::StopServer()
repair_timer.Deactivate();
tx_repair_pending = false;
}
encoder.Destroy();
if (NULL != encoder)
{
encoder->Destroy();
delete encoder;
encoder = NULL;
}
acking_node_tree.Destroy();
cc_node_list.Destroy();
// Iterate tx_table and release objects
@ -749,6 +768,7 @@ void NormSession::Serve()
}
}
}
//ASSERT(stream->IsPending() || stream->IsRepairPending());
if (!posted_tx_queue_empty && stream->IsPending() && !stream->IsClosing())
{
//data_active = false;
@ -778,9 +798,7 @@ void NormSession::Serve()
{
data_active = false; // (TBD) should we wait until the flush process completes before setting false???
posted_tx_queue_empty = true;
Notify(NormController::TX_QUEUE_EMPTY,
(NormServerNode*)NULL,
(NormObject*)NULL);
Notify(NormController::TX_QUEUE_EMPTY, (NormServerNode*)NULL, (NormObject*)NULL);
// (TBD) Was session deleted?
return;
}
@ -1103,7 +1121,8 @@ void NormSession::QueueMessage(NormMsg* msg)
tx_timer.SetInterval(0.0);
ActivateTimer(tx_timer);
}
if (msg) message_queue.Append(msg);
if (msg)
message_queue.Append(msg);
} // end NormSesssion::QueueMessage(NormMsg& msg)
@ -1339,29 +1358,52 @@ void NormSession::DeleteTxObject(NormObject* obj)
obj->Release();
} // end NormSession::DeleteTxObject()
void NormSession::SetTxCacheBounds(NormObjectSize sizeMax,
bool NormSession::SetTxCacheBounds(NormObjectSize sizeMax,
unsigned long countMin,
unsigned long countMax)
{
bool result = true;
tx_cache_size_max = sizeMax;
tx_cache_count_min = (countMin < countMax) ? countMin : countMax;
tx_cache_count_max = (countMax > countMin) ? countMax : countMin;
// Now trim the tx_table as needed
if (IsServer())
{
// Trim/resize the tx_table and tx masks as needed
unsigned long count = tx_table.Count();
while ((count >= tx_cache_count_min) &&
((count >= tx_cache_count_max) ||
(tx_table.GetSize() > tx_cache_size_max)))
{
// Remove oldest non-pending
// Remove oldest (hopefully non-pending ) object
NormObject* oldest = tx_table.Find(tx_table.RangeLo());
ASSERT(oldest);
Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, oldest);
DeleteTxObject(oldest);
count = tx_table.Count();
}
if (tx_cache_count_max < DEFAULT_TX_CACHE_MAX)
countMax = DEFAULT_TX_CACHE_MAX;
else
countMax = tx_cache_count_max;
if (countMax != tx_table.GetRangeMax())
{
tx_table.SetRangeMax((UINT16)countMax);
result = tx_pending_mask.Resize(countMax);
result &= tx_repair_mask.Resize(countMax);
if (!result)
{
countMax = tx_pending_mask.GetSize();
if (tx_repair_mask.GetSize() < (INT32)countMax)
countMax = tx_repair_mask.GetSize();
if (tx_cache_count_max > countMax)
tx_cache_count_max = countMax;
if (tx_cache_count_min > tx_cache_count_max)
tx_cache_count_min = tx_cache_count_max;
}
}
}
return result;
} // end NormSession::SetTxCacheBounds()
NormBlock* NormSession::ServerGetFreeBlock(NormObjectId objectId,
@ -1550,14 +1592,10 @@ void NormTrace(const struct timeval& currentTime,
if (data.IsData() && data.IsStream())
{
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_MSG_START))
if (0 != NormDataMsg::ReadStreamPayloadMsgStart(data.GetPayload()))
UINT16 msgStartOffset = NormDataMsg::ReadStreamPayloadMsgStart(data.GetPayload());
if (0 != msgStartOffset)
{
// (TBD) use "payload_msg_start" value for this ...
// We usually use the first two bytes of "messages"
// as a "message length" header
UINT16 x;
memcpy(&x, data.GetPayloadData(), 2);
DMSG(0, "start word>%hu ", ntohs(x));
DMSG(0, "start word>%hu ", msgStartOffset - 1);
}
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_STREAM_END))
if (0 == NormDataMsg::ReadStreamPayloadLength(data.GetPayload()))
@ -1923,13 +1961,11 @@ void NormSession::ServerHandleCCFeedback(struct timeval currentTime,
NormCCNode* node = (NormCCNode*)cc_node_list.FindNodeById(nodeId);
if (node) ccRtt = node->UpdateRtt(ccRtt);
if (0 == (ccFlags & NormCC::START))
{
// slow start has ended
cc_slow_start = false;
// adjust rate using current rtt for node
bool ccSlowStart = (0 != (ccFlags & NormCC::START));
if (!ccSlowStart)
ccRate = CalculateRate(nominal_packet_size, ccRtt, ccLoss);
}
//DMSG(0, "NormSession::ServerHandleCCFeedback() node>%lu rate>%lf (rtt>%lf loss>%lf slow_start>%d)\n",
// nodeId, ccRate * 8.0 / 1000.0, ccRtt, ccLoss, (0 != (ccFlags & NormCC::START)));
@ -1959,6 +1995,7 @@ void NormSession::ServerHandleCCFeedback(struct timeval currentTime,
next->SetCCSequence(ccSequence);
next->SetActive(true);
next->SetFeedbackTime(currentTime);
cc_slow_start = ccSlowStart; // use CLR status for our slow_start state
if (savedId == nodeId)
{
// This was feedback from the current CLR
@ -2559,6 +2596,7 @@ void NormSession::ServerHandleNackMessage(const struct timeval& currentTime, Nor
} // end if (freshBlock)
ASSERT(NULL != block);
// If stream && explicit data repair, lock the data for retransmission
// (TBD) this use of "ndata" needs to be replaced for dynamically shortened blocks
if (object->IsStream() && (nextSegmentId < ndata))
{
bool attemptLock = true;
@ -3103,9 +3141,18 @@ bool NormSession::OnTxTimeout(ProtoTimer& /*theTimer*/)
}
else
{
// (TBD) should we check the type of error that occurred
// and take some smarter action here (e.g. re-open our sockets?)
// Requeue the message for another try
if (!advertise_repairs)
message_queue.Prepend(msg);
// Make sure the tx_timer interval is non-ZERO
// (this avoids a sort of infinite loop that can occur
// under certain conditions)
if (tx_rate > 0.0)
tx_timer.SetInterval(msg->GetLength() / tx_rate);
else if (0.0 == tx_timer.GetInterval())
tx_timer.SetInterval(0.1);
}
return true; // reinstall tx_timer
}
@ -3203,12 +3250,17 @@ bool NormSession::SendMessage(NormMsg& msg)
msg.SetSourceId(local_node_id);
UINT16 msgSize = msg.GetLength();
bool result = true;
// Possibly drop some tx messages for testing purposes
bool drop = (UniformRand(100.0) < tx_loss_rate);
if (drop || (isClientMsg && client_silent))
{
//DMSG(0, "TX MESSAGE DROPPED! (tx_loss_rate:%lf\n", tx_loss_rate);
if (!(isClientMsg && client_silent))
{
// Update sent rate tracker even if dropped (for testing/debugging)
sent_accumulator += msgSize;
nominal_packet_size += 0.05 * (((double)msgSize) - nominal_packet_size);
}
}
else
{
@ -3228,12 +3280,11 @@ bool NormSession::SendMessage(NormMsg& msg)
}
else
{
DMSG(8, "NormSession::SendMessage() sendto() error\n");
result = false;
DMSG(8, "NormSession::SendMessage() sendto() error: %s\n", GetErrorString());
tx_sequence--;
return false;
}
}
if (result)
{
if (isProbe)
{
probe_pending = false;
@ -3257,11 +3308,6 @@ bool NormSession::SendMessage(NormMsg& msg)
}
}
return true;
}
else
{
return false;
}
} // end NormSession::SendMessage()
void NormSession::SetGrttProbingInterval(double intervalMin, double intervalMax)
@ -3503,7 +3549,7 @@ bool NormSession::OnProbeTimeout(ProtoTimer& /*theTimer*/)
feedbackAge += 1.0e-06*((double)(currentTime.tv_usec - feedbackTime.tv_usec));
else
feedbackAge -= 1.0e-06*((double)(feedbackTime.tv_usec - currentTime.tv_usec));
double maxFeedbackAge = 5 * grtt_advertised;
double maxFeedbackAge = 5 * MAX(grtt_advertised, next->GetRtt());
// Safety bound to compensate for computer clock coarseness
// and possible sluggish feedback from slower machines
// at higher norm data rates (keeps rate from being
@ -3511,9 +3557,13 @@ bool NormSession::OnProbeTimeout(ProtoTimer& /*theTimer*/)
if (maxFeedbackAge <(10*NORM_TICK_MIN))
maxFeedbackAge = (10*NORM_TICK_MIN);
if (feedbackAge > maxFeedbackAge)
{
DMSG(6, "Deactivating cc node feedbackAge:%lf sec maxAge:%lf sec\n",
feedbackAge, maxFeedbackAge);
next->SetActive(false);
}
}
}
AdjustRate(false);

View File

@ -101,6 +101,9 @@ class NormSession
static const double DEFAULT_GSIZE_ESTIMATE;
static const UINT16 DEFAULT_NDATA;
static const UINT16 DEFAULT_NPARITY;
static const UINT16 DEFAULT_TX_CACHE_MIN;
static const UINT16 DEFAULT_TX_CACHE_MAX;
enum ProbingMode {PROBE_NONE, PROBE_PASSIVE, PROBE_ACTIVE};
enum AckingStatus
{
@ -129,7 +132,7 @@ class NormSession
// (TBD) call tx_socket->SetFlowLabel() to set traffic class for IPv6 sockets
// (or should we have ProtoSocket::SetTOS() do this for us?)
bool result = tx_socket->IsOpen() ? tx_socket->SetTOS(theTOS) : true;
tos = result ? theTOS : ttl;
tos = result ? theTOS : tos;
return result;
}
bool SetLoopback(bool state)
@ -177,7 +180,7 @@ class NormSession
void SetGrttMax(double grttMax) {grtt_max = grttMax;}
void SetTxRateBounds(double rateMin, double rateMax);
void SetTxCacheBounds(NormObjectSize sizeMax,
bool SetTxCacheBounds(NormObjectSize sizeMax,
unsigned long countMin,
unsigned long countMax);
void Notify(NormController::Event event,
@ -247,6 +250,13 @@ class NormSession
void ServerSetExtraParity(UINT16 extraParity)
{extra_parity = extraParity;}
// EMCON Server (useful when there are silent receivers)
// (NORM_INFO is redundantly sent)
void SndrSetEmcon(bool state)
{sndr_emcon = true;}
bool SndrEmcon() const
{return sndr_emcon;}
bool ServerGetFirstPending(NormObjectId& objectId)
{
UINT32 index;
@ -282,7 +292,7 @@ class NormSession
}
void ServerEncode(const char* segment, char** parityVectorList)
{encoder.Encode(segment, parityVectorList);}
{encoder->Encode(segment, parityVectorList);}
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
@ -313,16 +323,30 @@ class NormSession
{return remote_server_buffer_size;}
void ClientSetUnicastNacks(bool state) {unicast_nacks = state;}
bool ClientGetUnicastNacks() const {return unicast_nacks;}
void ClientSetSilent(bool state)
{
client_silent = state;
receiver_low_delay = client_silent ? receiver_low_delay : false;
}
{client_silent = state;}
bool ClientIsSilent() const {return client_silent;}
void ReceiverSetLowDelay(bool state)
{receiver_low_delay = client_silent ? state : false;}
bool ReceiverIsLowDelay() {return receiver_low_delay;}
void RcvrSetIgnoreInfo(bool state)
{rcvr_ignore_info = state;}
bool RcvrIgnoreInfo() const
{return rcvr_ignore_info;}
// "-1" corresponds to typical operation where source data for
// partially received FEC blocks are only provided to the app
// when buffer constraints require it.
// Otherwise, the "maxDelay" corresponds to the max number
// of FEC blocks the receiver waits before passing partially
// received blocks to the app.
// Note a "maxDelay == 0" provides _no_ protection from
// out-of-order received packets!
void RcvrSetMaxDelay(INT32 maxDelay)
{rcvr_max_delay = maxDelay;}
bool RcvrIsLowDelay()
{return (ClientIsSilent() && (rcvr_max_delay >= 0));}
INT32 RcvrGetMaxDelay() const
{return rcvr_max_delay;}
NormObject::NackingMode ClientGetDefaultNackingMode() const
{return default_nacking_mode;}
@ -401,8 +425,8 @@ class NormSession
bool notify_pending;
ProtoTimer tx_timer;
UINT16 tx_port;
ProtoSocket* tx_socket;
ProtoSocket tx_socket_actual;
ProtoSocket* tx_socket;
ProtoSocket rx_socket;
NormMessageQueue message_queue;
NormMessageQueue message_pool;
@ -431,6 +455,7 @@ class NormSession
UINT16 nparity;
UINT16 auto_parity;
UINT16 extra_parity;
bool sndr_emcon;
NormObjectTable tx_table;
ProtoSlidingMask tx_pending_mask;
@ -438,7 +463,7 @@ class NormSession
ProtoTimer repair_timer;
NormBlockPool block_pool;
NormSegmentPool segment_pool;
NormEncoder encoder;
NormEncoder* encoder;
NormObjectId next_tx_object_id;
unsigned int tx_cache_count_min;
@ -507,7 +532,8 @@ class NormSession
unsigned long remote_server_buffer_size;
bool unicast_nacks;
bool client_silent;
bool receiver_low_delay;
bool rcvr_ignore_info;
INT32 rcvr_max_delay;
NormServerNode::RepairBoundary default_repair_boundary;
NormObject::NackingMode default_nacking_mode;

View File

@ -1,5 +1,4 @@
#include "normSimAgent.h"
#include <errno.h>
// This NORM simulation agent includes support for providing transport
@ -8,7 +7,7 @@
NormSimAgent::NormSimAgent(ProtoTimerMgr& timerMgr,
ProtoSocket::Notifier& socketNotifier)
: session_mgr(timerMgr, socketNotifier), session(NULL),
: msg_sink(NULL), session_mgr(timerMgr, socketNotifier), session(NULL),
address(NULL), port(0), ttl(3),
tx_rate(NormSession::DEFAULT_TRANSMIT_RATE),
cc_enable(false), unicast_nacks(false), silent_client(false),
@ -22,7 +21,7 @@ NormSimAgent::NormSimAgent(ProtoTimerMgr& timerMgr,
tx_repeat_count(0), tx_repeat_interval(0.0),
stream(NULL), auto_stream(false), push_mode(false),
flush_mode(NormStreamObject::FLUSH_PASSIVE),
mgen(NULL), msg_sync(false), mgen_bytes(0), mgen_pending_bytes(0),
msg_sync(false), mgen_bytes(0), mgen_pending_bytes(0),
tracing(false), tx_loss(0.0), rx_loss(0.0)
{
// Bind NormSessionMgr to this agent and simulation environment
@ -586,6 +585,8 @@ bool NormSimAgent::SendMessage(unsigned int len, const char* txBuffer)
DMSG(0, "NormSimAgent::SendMessage() session not started!\n");
return false;
}
return true;
} // end NormSimAgent::SendMessage()
void NormSimAgent::OnInputReady()
@ -722,7 +723,7 @@ void NormSimAgent::Notify(NormController::Event event,
case NormObject::STREAM:
{
// Read the stream when it's updated
if (mgen)
if (msg_sink)
{
bool dataReady = true;
while (dataReady)
@ -792,6 +793,7 @@ void NormSimAgent::Notify(NormController::Event event,
DMSG(0, "NormSimAgent::Notify(3) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
break;
}
}
@ -800,12 +802,7 @@ void NormSimAgent::Notify(NormController::Event event,
ProtoAddress srcAddr;
srcAddr.ResolveFromString(server->GetAddress().GetHostString());
srcAddr.SetPort(server->GetAddress().GetPort());
#ifdef OPNET // JPH 4/11/06 Use packet stream to send msg to mgen rather than direct call on mgen method.
HandleMgenMessage(mgen_buffer, mgen_bytes, srcAddr);
#else
mgen->HandleMgenMessage(mgen_buffer, mgen_bytes, srcAddr);
#endif OPNET
msg_sink->HandleMessage(mgen_buffer,mgen_bytes,srcAddr);
mgen_bytes = 0;
}
} // end if (mgen_pending_bytes)

View File

@ -1,13 +1,14 @@
#ifndef _NORM_SIM_AGENT
#define _NORM_SIM_AGENT
// normSimAgent.h - Generic (base class) NORM simulation agent
#include "normSession.h"
#include "protokit.h"
#include "mgen.h" // for MGEN instance attachment
#include "protoSimAgent.h"
// Base class for Norm simulation agents (e.g. ns-2, OPNET, etc)
class NormSimAgent : public NormController
class NormSimAgent : public NormController, public ProtoMessageSink
{
public:
virtual ~NormSimAgent();
@ -20,8 +21,8 @@ class NormSimAgent : public NormController
bool IsActive() {return (NULL != session);}
void Stop();
bool SendMessage(unsigned int len, const char* txBuffer);
void AttachMgen(Mgen* mgenInstance) {mgen = mgenInstance;}
protected:
NormSimAgent(ProtoTimerMgr& timerMgr,
@ -29,10 +30,14 @@ class NormSimAgent : public NormController
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
CmdType CommandType(const char* cmd);
virtual unsigned long GetAgentId() = 0;
/* JPH 4/11/06 Use packet stream instead of direct call on mgen process */
void HandleMgenMessage(char* buffer,
ProtoMessageSink* msg_sink;
#ifdef OPNET
void HandleMessage(char* buffer,
unsigned int len,
const ProtoAddress& srcAddr);
void SetSink(ProtoMessageSink* sink){msg_sink=sink;}
#endif //OPNET
private:
void OnInputReady();
@ -87,12 +92,10 @@ class NormSimAgent : public NormController
char* tx_msg_buffer;
unsigned int tx_msg_len;
unsigned int tx_msg_index;
Mgen* mgen; // mgen receiver
char mgen_buffer[64];
bool msg_sync;
unsigned int mgen_bytes;
unsigned int mgen_pending_bytes;
ProtoTimer interval_timer;
// protocol debug parameters
@ -102,3 +105,4 @@ class NormSimAgent : public NormController
}; // end class NormSimAgent
#endif // NORM_SIM_AGENT

View File

@ -85,7 +85,7 @@ int main(int argc, char* argv[])
NormSetGrttEstimate(session, 0.001); // 1 msec initial grtt
NormSetTransmitRate(session, 1.0e+05); // in bits/second
NormSetTransmitRate(session, 1.0e+07); // in bits/second
// Uncomment to enable TCP-friendly congestion control (overrides NormSetTransmitRate())
//NormSetCongestionControl(session, true);
@ -114,7 +114,7 @@ int main(int argc, char* argv[])
//NormSetSilentReceiver(session, true);
// Uncomment this line to participate as a receiver
NormStartReceiver(session, 1024*1024);
//NormStartReceiver(session, 1024*1024);
// Uncomment to set large rx socket buffer size
// (might be needed for high rate sessions)
@ -124,7 +124,7 @@ int main(int argc, char* argv[])
NormSessionId sessionId = (NormSessionId)rand();
// Uncomment the following line to start sender
//NormStartSender(session, sessionId, 1024*1024, 1400, 64, 8);
NormStartSender(session, sessionId, 1024*1024, 1400, 64, 8);
//NormSetAutoParity(session, 6);
@ -146,7 +146,7 @@ int main(int argc, char* argv[])
// Uncomment this line to send a stream instead of the file
//stream = NormStreamOpen(session, 4*1024*1024);
stream = NormStreamOpen(session, 4*1024*1024);
// NORM_FLUSH_PASSIVE automatically flushes full writes to
// the stream.

434
common/normThreadTest.cpp Normal file
View File

@ -0,0 +1,434 @@
// This is a test application for experimenting
// with the NORM API implementation during its
// development. A better-documented and complete
// example of the NORM API usage will be provided
// when the NORM API is more complete.
// This tests the use of the NORM API in a multi-threaded app
#include "normApi.h"
#include "protokit.h" // for protolib debug, stuff, etc
#include <stdio.h>
#include <stdlib.h> // for srand()
#ifdef UNIX
#include <unistd.h> // for "sleep()"
#endif // UNIX
class NormThreadApp : public ProtoApp
{
public:
NormThreadApp();
virtual ~NormThreadApp();
// Overrides from ProtoApp or NsProtoSimAgent base
bool OnStartup(int argc, const char*const* argv);
bool ProcessCommands(int argc, const char*const* argv);
void OnShutdown();
bool OnCommand(const char* cmd, const char* val);
private:
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
CmdType CommandType(const char* cmd);
static const char* const cmd_list[];
static void DoNormEvent(ProtoDispatcher::Descriptor descriptor,
ProtoDispatcher::Event theEvent,
const void* userData);
void OnNormEvent();
static void DoWorkerEvent(const void* clientData);
void WorkerReadStream();
enum {MSG_SIZE_MAX = 1024};
bool OnTxTimeout(ProtoTimer& timer);
// Our NORM API handles
NormInstanceHandle norm_instance;
NormSessionHandle norm_session;
NormObjectHandle norm_tx_stream;
NormObjectHandle norm_rx_stream;
bool sender;
bool receiver;
// This timer controls our message tx rate
ProtoTimer tx_msg_timer;
char tx_msg_buffer[MSG_SIZE_MAX];
unsigned int tx_msg_length;
unsigned int tx_msg_index;
// We use a ProtoDispatcher for a "worker" thread
// to handle NORM events for a specific session
ProtoDispatcher worker_thread_dispatcher;
}; // end class NormThreadApp
// Our application instance
PROTO_INSTANTIATE_APP(NormThreadApp)
NormThreadApp::NormThreadApp()
: norm_instance(NORM_INSTANCE_INVALID),
norm_session(NORM_SESSION_INVALID),
norm_tx_stream(NORM_OBJECT_INVALID), norm_rx_stream(NORM_OBJECT_INVALID),
tx_msg_length(0), tx_msg_index(0)
{
tx_msg_timer.SetListener(this, &NormThreadApp::OnTxTimeout);
tx_msg_timer.SetInterval(0.0015);
tx_msg_timer.SetRepeat(-1);
worker_thread_dispatcher.SetPromptCallback(DoWorkerEvent, this);
}
NormThreadApp::~NormThreadApp()
{
}
bool NormThreadApp::OnStartup(int argc, const char*const* argv)
{
// 1) (TBD) Process any command-line options
if (!ProcessCommands(argc, argv))
{
TRACE("error with commands\n");
return false;
}
// 2) Create a Norm API instance and "generic" input handler for notifications
norm_instance = NormCreateInstance();
ASSERT(NORM_INSTANCE_INVALID != norm_instance);
SetDebugLevel(2);
// Set a callback that will call NormGetNextEvent()
if (!dispatcher.InstallGenericInput(NormGetDescriptor(norm_instance), DoNormEvent, this))
{
DMSG(0, "NormThreadApp::OnStartup() InstallGenericInput() error\n");
NormDestroyInstance(norm_instance);
return false;
}
// 3) Create a "custom" unique node identifier
// Here's a trick to generate a _hopefully_ unique NormNodeId
// based on an XOR of the system's IP address and the process id.
// (We use ProtoAddress::GetEndIdentifier() to get the local
// "default" IP address for the system)
// (Note that passing "NORM_NODE_ANY" to the last arg of
// NormCreateSession() does a similar thing but without
// the processId XOR ... perhaps we should add the processId
// hack to that default "NORM_NODE_ANY" local NormNodeId picker???
ProtoAddress localAddr;
if (!localAddr.ResolveLocalAddress())
{
fprintf(stderr, "normTest: error resolving local IP address\n");
OnShutdown();
return false;
}
NormNodeId localId = localAddr.EndIdentifier();
#ifdef WIN32
DWORD processId = GetCurrentProcessId();
#else
pid_t processId = getpid();
#endif // if/else WIN32/UNIX
localId ^= (NormNodeId)processId;
// If needed, permutate to a valid, random NormNodeId
while ((NORM_NODE_ANY == localId) ||
(NORM_NODE_NONE == localId))
{
localId ^= (NormNodeId)rand();
}
//localId = 15; // for testing purposes
// 4) Create a NORM session
norm_session = NormCreateSession(norm_instance,
"224.1.1.1",
6001,
localId);
ASSERT(NORM_SESSION_INVALID != norm_session);
NormSetGrttEstimate(norm_session, 0.250); // 1 msec initial grtt
NormSetTransmitRate(norm_session, 10.0e+06); // in bits/second
//NormSetLoopback(norm_session, true);
// 5) If sender pick a random "session id", start sender, and create a stream
// We use a random "sessionId"
if (sender)
{
NormSessionId sessionId = (NormSessionId)rand();
//NormStartSender(norm_session, sessionId, 1024*1024, 1400, 64, 8);
NormStartSender(norm_session, sessionId, 1024*1024, 1400, 64, 0);
norm_tx_stream = NormStreamOpen(norm_session, 1024*1024);
// Activate tx timer and force first message transmission
ActivateTimer(tx_msg_timer);
OnTxTimeout(tx_msg_timer);
}
// 6) If receiver, start receiver
if (receiver)
{
NormStartReceiver(norm_session, 1024*1024);
worker_thread_dispatcher.StartThread();
}
return true;
} // end NormThreadApp::OnStartup()
void NormThreadApp::OnShutdown()
{
if (tx_msg_timer.IsActive()) tx_msg_timer.Deactivate();
if (NORM_INSTANCE_INVALID != norm_instance)
{
dispatcher.RemoveGenericInput(NormGetDescriptor(norm_instance));
NormDestroyInstance(norm_instance);
norm_instance = NORM_INSTANCE_INVALID;
}
worker_thread_dispatcher.Stop();
} // end NormThreadApp::OnShutdown()
bool NormThreadApp::OnTxTimeout(ProtoTimer& /*timer*/)
{
//TRACE("enter NormThreadApp::OnTxTimeout() ...\n");
if (0 == tx_msg_length)
{
// Send a new message
unsigned int sendCount = 1;
sprintf(tx_msg_buffer, "normThreadTest says hello %u ", sendCount);
unsigned int msgLength = strlen(tx_msg_buffer);
memset(tx_msg_buffer + msgLength, 'a', 900 - msgLength);
tx_msg_length = 900;
tx_msg_index = 0;
}
unsigned int bytesHave = tx_msg_length - tx_msg_index;
unsigned int bytesWritten =
NormStreamWrite(norm_tx_stream, tx_msg_buffer + tx_msg_index, tx_msg_length - tx_msg_index);
//TRACE("wrote %u bytes to stream ...\n", bytesWritten);
if (bytesWritten == bytesHave)
{
tx_msg_length = 0;
}
else
{
// We filled the stream buffer
tx_msg_index += bytesWritten;
tx_msg_timer.Deactivate();
}
return true;
} // end NormThreadApp::OnTxTimeout()
void NormThreadApp::DoNormEvent(ProtoDispatcher::Descriptor descriptor,
ProtoDispatcher::Event theEvent,
const void* userData)
{
NormThreadApp* theApp = reinterpret_cast<NormThreadApp*>((void*)userData);
theApp->OnNormEvent();
} // end NormThreadApp::DoNormEvent()
void NormThreadApp::OnNormEvent()
{
static unsigned long updateCount = 0;
NormEvent theEvent;
if (NormGetNextEvent(norm_instance, &theEvent))
{
switch (theEvent.type)
{
case NORM_TX_QUEUE_EMPTY:
case NORM_TX_QUEUE_VACANCY:
/*if (NORM_TX_QUEUE_VACANCY == theEvent.type)
TRACE("NORM_TX_QUEUE_VACANCY ...\n");
else
TRACE("NORM_TX_QUEUE_EMPTY ...\n");*/
if (!tx_msg_timer.IsActive())
{
ActivateTimer(tx_msg_timer);
OnTxTimeout(tx_msg_timer);
}
break;
case NORM_GRTT_UPDATED:
break;
case NORM_REMOTE_SENDER_NEW:
case NORM_REMOTE_SENDER_ACTIVE:
break;
case NORM_RX_OBJECT_NEW:
TRACE("NORM_RX_OBJECT_NEW ...\n");
norm_rx_stream = theEvent.object;
break;
case NORM_RX_OBJECT_UPDATED:
//TRACE("NORM_RX_OBJECT_UPDATED ...\n");
updateCount++;
if ((updateCount % 100) == 0)
TRACE("updateCount:%lu\n", updateCount);
worker_thread_dispatcher.PromptThread();
break;
case NORM_RX_OBJECT_ABORTED:
TRACE("NORM_RX_OBJECT_ABORTED ...\n");
break;
default:
TRACE("UNHANDLED NORM EVENT : %d...\n", theEvent.type);
break;
}
}
else
{
DMSG(0, "NormThreadApp::OnNormEvent() NormGetNextEvent() error?\n");
}
} // end NormThreadApp::OnNormEvent()
void NormThreadApp::DoWorkerEvent(const void* clientData)
{
NormThreadApp* theApp = (NormThreadApp*)clientData;
theApp->WorkerReadStream();
} // end NormThreadApp::DoWorkerEvent()
void NormThreadApp::WorkerReadStream()
{
unsigned int loopCount = 0;
static unsigned long readCount = 0;
char rxBuffer[2048];
unsigned int bytesRead = 1400;
do
{
if (NormStreamRead(norm_rx_stream, rxBuffer, &bytesRead))
{
//TRACE("read %u bytes from stream ...\n", bytesRead);
if (0 != bytesRead)
{
readCount++;
if ((readCount % 100) == 0)
TRACE("readCount:%lu\n", readCount);
bytesRead = 1400;
}
}
else
{
TRACE("NormThreadApp::WorkerReadStream() stream broken!\n");
bytesRead = 0;
}
loopCount++;
} while (0 != bytesRead);
//TRACE("loopCount: %lu\n", loopCount);
} // end NormThreadApp::WorkerReadStream()
const char* const NormThreadApp::cmd_list[] =
{
"+debug", // debug level
"-send",
"-recv",
NULL
}; // end NormThreadApp::cmd_list[]
bool NormThreadApp::OnCommand(const char* cmd, const char* val)
{
size_t cmdlen = strlen(cmd);
if (!strncmp("debug", cmd, cmdlen))
{
}
else if (!strncmp("send", cmd, cmdlen))
{
sender = true;
}
else if (!strncmp("recv", cmd, cmdlen))
{
receiver = true;
}
else if (!strncmp("debug", cmd, cmdlen))
{
DMSG(0, "NormThreadApp::OnCommand(%s) unknown command\n", cmd);
return false;
}
return true;
} // end NormThreadApp::ProcessCommands()
NormThreadApp::CmdType NormThreadApp::CommandType(const char* cmd)
{
if (!cmd) return CMD_INVALID;
size_t len = strlen(cmd);
bool matched = false;
CmdType type = CMD_INVALID;
const char* const* nextCmd = cmd_list;
while (*nextCmd)
{
if (!strncmp(cmd, *nextCmd+1, len))
{
if (matched)
{
// ambiguous command (command should match only once)
return CMD_INVALID;
}
else
{
matched = true;
if ('+' == *nextCmd[0])
type = CMD_ARG;
else
type = CMD_NOARG;
}
}
nextCmd++;
}
return type;
} // end NormThreadApp::CommandType()
bool NormThreadApp::ProcessCommands(int argc, const char*const* argv)
{
int i = 1;
while ( i < argc)
{
CmdType cmdType = CommandType(argv[i]);
switch (cmdType)
{
case CMD_INVALID:
DMSG(0, "NormThreadApp::ProcessCommands() Invalid command:%s\n",
argv[i]);
return false;
case CMD_NOARG:
if (!OnCommand(argv[i], NULL))
{
DMSG(0, "NormThreadApp::ProcessCommands() OnCommand(%s) error\n",
argv[i]);
return false;
}
i++;
break;
case CMD_ARG:
if (!OnCommand(argv[i], argv[i+1]))
{
DMSG(0, "NormThreadApp::ProcessCommands() OnCommand(%s, %s) error\n",
argv[i], argv[i+1]);
return false;
}
i += 2;
break;
}
}
return true;
} // end NormThreadApp::ProcessCommands()

View File

@ -32,6 +32,6 @@
#ifndef _NORM_VERSION
#define _NORM_VERSION
#define VERSION "1.3b9"
#define VERSION "1.4b2"
#endif // _NORM_VERSION

View File

@ -1,6 +1,6 @@
/*********************************************************************
*
* AUTHORIZATION TO USE AND DISTRIBUTE
* Authorization TO USE AND DISTRIBUTE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that:
@ -33,8 +33,8 @@
#include "ip.h" // for hdr_ip def
#include "flags.h" // for hdr_flags def
#include "nsMgenAgent.h"
#include "nsNormAgent.h"
static class NsNormAgentClass : public TclClass
{
public:
@ -56,6 +56,7 @@ NsNormAgent::~NsNormAgent()
bool NsNormAgent::OnStartup(int argc, const char*const* argv)
{
if (ProcessCommands(argc, argv))
{
return true;
@ -89,10 +90,10 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
return false;
}
Tcl& tcl = Tcl::instance();
NsMgenAgent* mgenAgent = dynamic_cast<NsMgenAgent*> (tcl.lookup(argv[i]));
if (mgenAgent)
msg_sink = dynamic_cast<ProtoMessageSink*>(tcl.lookup(argv[i]));
if (msg_sink)
{
AttachMgen(mgenAgent->GetMgenInstance());
i++;
continue;
}
@ -145,13 +146,3 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
return true;
} // end NsNormAgent::ProcessCommands()
bool NsNormAgent::SendMgenMessage(const char* txBuffer,
unsigned int len,
const ProtoAddress& /*dstAddr*/)
{
return SendMessage(len, txBuffer);
} // end NsNormAgent::SendMgenMessage()

View File

@ -36,15 +36,13 @@
#include "nsProtoSimAgent.h" // from Protolib
#include "normSimAgent.h"
#include "nsMgenAgent.h"
// The "NsProtoAgent" is based on the ns Agent class.
// This lets us have a send/recv attachment to the NS
// simulation environment
// IMPORTANT NOTE! NsProtoAgent must be listed _first_ here
// (because we can't dynamic_cast install_data void* pointers)
class NsNormAgent : public NsProtoSimAgent, public NormSimAgent, public MgenSink
class NsNormAgent : public NsProtoSimAgent, public NormSimAgent
{
public:
NsNormAgent();
@ -55,12 +53,14 @@ class NsNormAgent : public NsProtoSimAgent, public NormSimAgent, public MgenSink
virtual bool ProcessCommands(int argc, const char*const* argv);
virtual void OnShutdown();
// NormSimAgent override
// NormSimAgent overrides
unsigned long GetAgentId() {return (unsigned long)addr();}
// MgenSink override
bool SendMgenMessage(const char* txBuffer,
unsigned int len,
const ProtoAddress& dstAddr);
bool HandleMessage(const char* txBuffer, unsigned int len, const ProtoAddress& srcAddr)
{
return NormSimAgent::SendMessage(len,txBuffer);
}
}; // end class NsNormAgent

View File

@ -1,10 +1,10 @@
/* Process model C++ form file: norm_protolib.pr.cpp */
/* Portions of this file copyright 1992-2004 by OPNET Technologies, Inc. */
/* Portions of this file copyright 1992-2006 by OPNET Technologies, Inc. */
/* This variable carries the header into the object file */
const char norm_protolib_pr_cpp [] = "MIL_3_Tfile_Hdr_ 115A 30A op_runsim 7 448841E4 448841E4 1 apocalypse Jim@Hauser 0 0 none none 0 0 none 0 0 0 0 0 0 0 0 d2c 2 ";
const char norm_protolib_pr_cpp [] = "MIL_3_Tfile_Hdr_ 115A 30A op_runsim 7 44EC8462 44EC8462 1 apocalypse Jim@Hauser 0 0 none none 0 0 none 0 0 0 0 0 0 0 0 d50 3 ";
#include <string.h>
@ -116,7 +116,7 @@ class norm_protolib_state
IpT_Address dest_ip_addr ;
FILE* script_fp ;
int source ;
Ici* app_ici_v3_ptr ;
Ici* app_ici_ptr ;
/* FSM code */
void norm_protolib (OP_SIM_CONTEXT_ARG_OPT);
@ -188,7 +188,7 @@ VosT_Obtype norm_protolib_state::obtype = (VosT_Obtype)OPC_NIL;
#define dest_ip_addr pr_state_ptr->dest_ip_addr
#define script_fp pr_state_ptr->script_fp
#define source pr_state_ptr->source
#define app_ici_v3_ptr pr_state_ptr->app_ici_v3_ptr
#define app_ici_ptr pr_state_ptr->app_ici_ptr
/* These macro definitions will define a local variable called */
/* "op_sv_ptr" in each function containing a FIN statement. */
@ -781,11 +781,11 @@ void NormSimAgent::HandleMgenMessage(char* buffer,
op_prg_mem_copy_create, op_prg_mem_free, buflen);
op_ici_attr_set(app_ici_v3_ptr, "local_port", local_port);
op_ici_attr_set(app_ici_v3_ptr, "rem_port", srcAddr.GetPort());
op_ici_attr_set(app_ici_v3_ptr, "rem_addr", srcAddr.SimGetAddress());
op_ici_attr_set(app_ici_v3_ptr, "src_addr", srcAddr.SimGetAddress());
op_ici_install(app_ici_v3_ptr);
op_ici_attr_set(app_ici_ptr, "local_port", local_port);
op_ici_attr_set(app_ici_ptr, "rem_port", srcAddr.GetPort());
op_ici_attr_set(app_ici_ptr, "rem_addr", srcAddr.SimGetAddress());
op_ici_attr_set(app_ici_ptr, "src_addr", srcAddr.SimGetAddress());
op_ici_install(app_ici_ptr);
op_pk_send_forced(pkt, OUTSTRM_TO_MGEN);
@ -851,7 +851,7 @@ void NormSimAgent::HandleMgenMessage(char* buffer,
#undef dest_ip_addr
#undef script_fp
#undef source
#undef app_ici_v3_ptr
#undef app_ici_ptr
/* Access from C kernel using C linkage */
extern "C"
@ -976,7 +976,7 @@ norm_protolib_state::norm_protolib (OP_SIM_CONTEXT_ARG_OPT)
FSM_STATE_ENTER_UNFORCED_NOLABEL (1, "init", "norm_protolib [init enter execs]")
FSM_PROFILE_SECTION_IN ("norm_protolib [init enter execs]", state1_enter_exec)
{
app_ici_v3_ptr = op_ici_create ("udp_command_v3");
app_ici_ptr = op_ici_create ("sink_command");
/* Obtain the object ID of the surrounding norm processor. */
my_id = op_id_self ();
@ -1528,9 +1528,9 @@ _op_norm_protolib_svar (void * gen_ptr, const char * var_name, void ** var_p_ptr
*var_p_ptr = (void *) (&prs_ptr->source);
FOUT
}
if (strcmp ("app_ici_v3_ptr" , var_name) == 0)
if (strcmp ("app_ici_ptr" , var_name) == 0)
{
*var_p_ptr = (void *) (&prs_ptr->app_ici_v3_ptr);
*var_p_ptr = (void *) (&prs_ptr->app_ici_ptr);
FOUT
}
*var_p_ptr = (void *)OPC_NIL;

View File

@ -0,0 +1,41 @@
#ifndef _OPNET_NORM_PROCESS
#define _OPNET_NORM_PROCESS
// JPH NORM 11/21/2005
#include "opnet.h"
#include "opnetProtoSimProcess.h"
#include "normSimAgent.h" // this includes protokit.h
class OpnetNormProcess : public OpnetProtoSimProcess, public NormSimAgent, public OpnetProtoMessageSink
{
public:
OpnetNormProcess();
~OpnetNormProcess();
// OpnetProtoSimProcess's base class overrides
bool OnStartup(int argc, const char*const* argv);
bool ProcessCommands(int argc, const char*const* argv);
void OnShutdown();
void ReceivePacketMonitor(Ici* ici, Packet* pkt);
void TransmitPacketMonitor(Ici* ici, Packet* pkt);
bool SendMgenMessage(const char* txBuffer,
unsigned int len,
const ProtoAddress& dstAddr);
// NormSimAgent override
unsigned long GetAgentId() {return (unsigned long)addr();}
private:
IpT_Address addr();
}; // end class OpnetNormProcess
#endif // _OPNET_NORM_PROCESS

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,55 +0,0 @@
# This file contains the IP routing tables built by various routing protocols in
# the network model.
# It is intended to be used as a mechanism whereby dynamic routing information
# is recorded and reused to avoid running IP routing protocols in multiple
# simulations of the same network. This feature should be used only when router
# connectivities in the network (and hence their routing tables) do not change
# during the course of the simulation.
# This file is produced each time a simulation is run with the simulation
# attribute "Routing Table Export/Import" set to "Export". Multiple runs for
# the same network model will successively overwrite this file.
# The contents of this file can be imported into the network model by running a
# simulation with the simulation attribute "Routing Table Export/Import" to
# "Import".
# In order for this to work correctly, there should be no change in the network
# whose simulation produced this file, and the network that is importing it
# contents. In other words, the "Export" scenario and the "Import" scenario
# should be the same.
# Warning: Modification of this file by the user can lead to unexpected simulation
# results.
START_ROUTING_TABLE at 60.000000
#Module Object ID,Table Size,Number of Interfaces,Is static
508,5,0,0
#Module Hierarchical Name:
Campus Network.node_(0).olsr_protolib
#Interface Information: Interface,IP Address,Mask
0,-1,192.0.0.100,255.255.255.0
#OLSR Routing Table Contents:
#-----------------------------------------------------------------
# Dest Network,Dest Net Mask, Metric, Next Hop Addr,
#-----------------------------------------------------------------
192.0.0.101,255.255.255.255,1,192.0.0.101
END_ROUTING_TABLE
START_ROUTING_TABLE at 60.000000
#Module Object ID,Table Size,Number of Interfaces,Is static
1044,6,0,0
#Module Hierarchical Name:
Campus Network.node_(1).olsr_protolib
#Interface Information: Interface,IP Address,Mask
0,-1,192.0.0.101,255.255.255.0
#OLSR Routing Table Contents:
#-----------------------------------------------------------------
# Dest Network,Dest Net Mask, Metric, Next Hop Addr,
#-----------------------------------------------------------------
192.0.0.100,255.255.255.255,1,192.0.0.100
END_ROUTING_TABLE

63
unix/Makefile.arm-linux Normal file
View File

@ -0,0 +1,63 @@
#
# arm-linux Protean Makefile definitions
#
# 1) System specific additional libraries, include paths, etc
# (Where to find X11 libraries, etc)
#
SYSTEM_INCLUDES = -I/usr/local/arm/2.95.3/arm-linux/include
SYSTEM_LDFLAGS = -L/usr/local/arm/2.95.3/arm-linux/lib
SYSTEM_LIBS = -ldl
# 2) 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)
#
#add -DHAVE_IPV6 if you have ipv6 support
export SYSTEM_HAVES = -DARM -DLINUX -DHAVE_GETLOGIN -DHAVE_LOCKF \
-DHAVE_OLD_SIGNALHANDLER -DNO_SCM_RIGHTS -DHAVE_DIRFD -DHAVE_ASSERT $(NETSEC)
SYSTEM_SRC = linuxRouteMgr.cpp
SYSTEM = arm-linux
CC = arm-linux-g++
SYSTEM_CFLAGS = -Wall -Wcast-align -pedantic -fPIC
SYSTEM_SOFLAGS = -shared
RANLIB = arm-linux-ranlib
AR = arm-linux-ar
AS = arm-linux-as
LD = arm-linux-ld
NM = arm-linux-nm
STRIP = arm-linux-strip
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
include Makefile.common

View File

@ -81,6 +81,26 @@ TEST_OBJ = $(TEST_SRC:.cpp=.o)
normTest: $(TEST_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(TEST_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
# (normThreadTest) test of threaded use of NORM API
TTEST_SRC = $(COMMON)/normThreadTest.cpp
TTEST_OBJ = $(TTEST_SRC:.cpp=.o)
normThreadTest: $(TTEST_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(TTEST_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
# (normThreadTest2) alt test of threaded use of NORM API
TTEST2_SRC = $(UNIX)/normThreadTest2.cpp
TTEST2_OBJ = $(TTEST2_SRC:.cpp=.o)
normThreadTest2: $(TTEST2_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(TTEST2_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
# (npc) NORM Pre-Coder
PCODE_SRC = $(COMMON)/normPrecode.cpp
PCODE_OBJ = $(PCODE_SRC:.cpp=.o)
npc: $(PCODE_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(PCODE_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
# (raft) command-line reliable tunnel helper
RAFT_SRC = $(COMMON)/raft.cpp
RAFT_OBJ = $(RAFT_SRC:.cpp=.o)

View File

@ -43,7 +43,7 @@ SYSTEM_SRC =
SYSTEM = linux
export CC = g++
export SYSTEM_CFLAGS = -fPIC -Wall -pedantic -Wcast-align
export SYSTEM_CFLAGS = -fPIC -Wall -Wcast-align
export SYSTEM_SOFLAGS = -shared
export RANLIB = ranlib
export AR = ar

View File

@ -45,7 +45,7 @@ SYSTEM_SRC =
SYSTEM = macosx
CC = g++
SYSTEM_CFLAGS = -fPIC -Wall -pedantic -Wcast-align
SYSTEM_CFLAGS = -fPIC -Wall -Wcast-align
SYSTEM_SOFLAGS = -dynamiclib
RANLIB = ranlib
AR = ar

View File

@ -24,6 +24,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NormDll", "NormDll.vcproj",
{50D1D52B-702A-49FE-B490-9104FC72598D} = {50D1D52B-702A-49FE-B490-9104FC72598D}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npc", "npc.vcproj", "{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}"
ProjectSection(ProjectDependencies) = postProject
{50D1D52B-702A-49FE-B490-9104FC72598D} = {50D1D52B-702A-49FE-B490-9104FC72598D}
{D7B0023C-8798-4918-8DA0-05C9054D70B9} = {D7B0023C-8798-4918-8DA0-05C9054D70B9}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
@ -50,6 +56,10 @@ Global
{182006F3-188F-466E-89FE-8421C0478691}.Debug.Build.0 = Debug|Win32
{182006F3-188F-466E-89FE-8421C0478691}.Release.ActiveCfg = Release|Win32
{182006F3-188F-466E-89FE-8421C0478691}.Release.Build.0 = Release|Win32
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Debug.ActiveCfg = Debug|Win32
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Debug.Build.0 = Debug|Win32
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Release.ActiveCfg = Release|Win32
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection

Binary file not shown.

View File

@ -88,7 +88,7 @@
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\common;..\protolib\common;..\protolib\win32;NormDll3"
PreprocessorDefinitions="NORM_USE_DLL, PROTO_DEBUG, WIN32, HAVE_ASSERT"
PreprocessorDefinitions="NORM_USE_DLL;PROTO_DEBUG;WIN32;HAVE_ASSERT"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"

160
win32/npc.vcproj Normal file
View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="npc"
ProjectGUID="{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}"
RootNamespace="normTest"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\common,..\protolib\common,..\protolib\win32"
PreprocessorDefinitions="PROTO_DEBUG;HAVE_IPV6;HAVE_ASSERT;WIN32"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
BufferSecurityCheck="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Debug/normTest.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="iphlpapi.lib ws2_32.lib odbc32.lib odbccp32.lib"
LinkIncremental="0"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="./"
GenerateDebugInformation="TRUE"
SubSystem="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/normTest.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\common;..\protolib\common;..\protolib\win32;NormDll3"
PreprocessorDefinitions="NDEBUG;PROTO_DEBUG;HAVE_IPV6;HAVE_ASSERT;WIN32"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Release/normTest.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
IgnoreImportLibrary="FALSE"
AdditionalDependencies="iphlpapi.lib ws2_32.lib odbc32.lib odbccp32.lib"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories=""
SubSystem="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/normTest.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="..\common\normPrecode.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

Binary file not shown.