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) 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 (COMPLETED - reimplemented, adding tx_repair_pending index to use
instead of seeking each time) 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 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 Version 1.3b9
============= =============
- Fixed ProtoSocket WINVER stuff and NormApplicationMsg::SetContent() - Fixed ProtoSocket WINVER stuff and NormApplicationMsg::SetContent()

View File

@ -491,9 +491,14 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent)
} }
break; break;
case NORM_RX_OBJECT_UPDATED: 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; break;
}
default: default:
break; break;
} }
@ -1502,13 +1507,13 @@ bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle,
NORM_API_LINKAGE NORM_API_LINKAGE
void NormSetSilentReceiver(NormSessionHandle sessionHandle, void NormSetSilentReceiver(NormSessionHandle sessionHandle,
bool silent, bool silent,
bool lowDelay) INT32 maxDelay)
{ {
NormSession* session = (NormSession*)sessionHandle; NormSession* session = (NormSession*)sessionHandle;
if (session) if (session)
{ {
session->ClientSetSilent(silent); session->ClientSetSilent(silent);
session->ReceiverSetLowDelay(lowDelay); session->RcvrSetMaxDelay(maxDelay);
} }
} // end NormSetSilentReceiver() } // end NormSetSilentReceiver()

View File

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

View File

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

View File

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

View File

@ -37,50 +37,71 @@
class NormEncoder 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: public:
NormEncoder(); virtual ~NormEncoder();
~NormEncoder(); virtual bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize) = 0;
bool Init(int numParity, int vectorSize); virtual void Destroy() = 0;
void Destroy(); virtual void Encode(const char *dataVector, char **parityVectorList) = 0;
bool IsReady(){return (bool)(gen_poly != NULL);} }; // end class NormEncoder
// "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();
};
class NormDecoder 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 // Members
private: private:
int npar; // No. of parity packets (n-k) unsigned int npar; // No. of parity packets (n-k)
int vector_size; // Size of biggest vector to encode UINT16 vector_size; // Size of biggest vector to encode
unsigned char* lambda; // Erasure location polynomial ("2*npar" ints) unsigned char* lambda; // Erasure location polynomial ("2*npar" ints)
unsigned char** s_vec; // Syndrome vectors (pointers to "npar" vectors) unsigned char** s_vec; // Syndrome vectors (pointers to "npar" vectors)
unsigned char** o_vec; // Omega vectors (pointers to "npar" vectors) unsigned char** o_vec; // Omega vectors (pointers to "npar" vectors)
unsigned char* scratch; unsigned char* scratch;
// Methods
public: }; // end class NormDecoderRS8a
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();
};

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

View File

@ -11,14 +11,14 @@
#else #else
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif // if/else WIN32 #endif // if/else WIN32
#ifdef _WIN32_WCE #ifdef _WIN32_WCE
#include <stdio.h> #include <stdio.h>
#else #else
#include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#endif // if/else _WIN32_WCE #endif // if/else _WIN32_WCE
// From PROTOLIB // From PROTOLIB
@ -56,7 +56,7 @@ class NormFile
bool Lock(); bool Lock();
void Unlock(); void Unlock();
bool Rename(const char* oldName, const char* newName); bool Rename(const char* oldName, const char* newName);
bool Unlink(const char *path); static bool Unlink(const char *path);
void Close(); void Close();
bool IsOpen() const bool IsOpen() const
{ {
@ -71,6 +71,7 @@ class NormFile
bool Seek(Offset theOffset); bool Seek(Offset theOffset);
NormFile::Offset GetOffset() const {return (offset);} NormFile::Offset GetOffset() const {return (offset);}
NormFile::Offset GetSize() const; 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 helper methods
static NormFile::Type GetType(const char *path); 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))))); return ((unsigned char)(ceil(255.0 - (13.0*log(NORM_RTT_MAX/rtt)))));
} // end NormQuantizeRtt() } // 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

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

View File

@ -40,14 +40,13 @@ NormCCNode::~NormCCNode()
{ {
} }
const double NormServerNode::DEFAULT_NOMINAL_INTERVAL = 2*NormSession::DEFAULT_GRTT_ESTIMATE; const double NormServerNode::DEFAULT_NOMINAL_INTERVAL = 2*NormSession::DEFAULT_GRTT_ESTIMATE;
const double NormServerNode::ACTIVITY_INTERVAL_MIN = 1.0; // 1 second min activity timeout const double NormServerNode::ACTIVITY_INTERVAL_MIN = 1.0; // 1 second min activity timeout
NormServerNode::NormServerNode(class NormSession& theSession, NormNodeId nodeId) NormServerNode::NormServerNode(class NormSession& theSession, NormNodeId nodeId)
: NormNode(theSession, nodeId), instance_id(0), synchronized(false), sync_id(0), : 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), 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), retrieval_loc(NULL), retrieval_pool(NULL),
cc_sequence(0), cc_enable(false), cc_rate(0.0), cc_sequence(0), cc_enable(false), cc_rate(0.0),
rtt_confirmed(false), is_clr(false), is_plr(false), rtt_confirmed(false), is_clr(false), is_plr(false),
@ -228,20 +227,28 @@ bool NormServerNode::AllocateBuffers(UINT16 segmentSize,
} }
retrieval_index = 0; 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()); DMSG(0, "NormServerNode::AllocateBuffers() retrieval_loc allocation error: %s\n", GetErrorString());
Close(); Close();
return false; 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"); DMSG(0, "NormServerNode::AllocateBuffers() decoder init error\n");
Close(); Close();
return false; 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()); DMSG(0, "NormServerNode::AllocateBuffers() erasure_loc allocation error: %s\n", GetErrorString());
Close(); Close();
@ -262,7 +269,12 @@ void NormServerNode::FreeBuffers()
delete[] erasure_loc; delete[] erasure_loc;
erasure_loc = NULL; erasure_loc = NULL;
} }
decoder.Destroy(); if (NULL != decoder)
{
decoder->Destroy();
delete decoder;
decoder = NULL;
}
if (retrieval_loc) if (retrieval_loc)
{ {
delete[] retrieval_loc; delete[] retrieval_loc;
@ -270,7 +282,7 @@ void NormServerNode::FreeBuffers()
} }
if (retrieval_pool) if (retrieval_pool)
{ {
for (UINT16 i = 0; i < ndata; i++) for (unsigned int i = 0; i < ndata; i++)
{ {
if (retrieval_pool[i]) if (retrieval_pool[i])
{ {
@ -285,10 +297,9 @@ void NormServerNode::FreeBuffers()
NormObject* obj; NormObject* obj;
while ((obj = rx_table.Find(rx_table.RangeLo()))) while ((obj = rx_table.Find(rx_table.RangeLo())))
{ {
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
UINT16 objectId = obj->GetId(); UINT16 objectId = obj->GetId();
DeleteObject(obj); AbortObject(obj);
// We do the following to remember which objects were pending // We do the following to remember which _objects_ were pending
rx_pending_mask.Set(objectId); 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", DMSG(4, "NormServerNode::UpdateGrttEstimate() node>%lu server>%lu new grtt: %lf sec\n",
LocalNodeId(), GetId(), grtt_estimate); LocalNodeId(), GetId(), grtt_estimate);
// activity timer depends upon sender's 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; double activityInterval = 2*NORM_ROBUST_FACTOR*grtt_estimate;
if (activityInterval < ACTIVITY_INTERVAL_MIN) activityInterval = ACTIVITY_INTERVAL_MIN; if (activityInterval < ACTIVITY_INTERVAL_MIN) activityInterval = ACTIVITY_INTERVAL_MIN;
activity_timer.SetInterval(activityInterval); activity_timer.SetInterval(activityInterval);
@ -361,10 +373,12 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
cc.GetSendTime(grtt_send_time); cc.GetSendTime(grtt_send_time);
cc_sequence = cc.GetCCSequence(); cc_sequence = cc.GetCCSequence();
NormCCRateExtension ext; NormCCRateExtension ext;
bool hasCCRateExtension = false;
while (cc.GetNextExtension(ext)) while (cc.GetNextExtension(ext))
{ {
if (NormHeaderExtension::CC_RATE == ext.GetType()) if (NormHeaderExtension::CC_RATE == ext.GetType())
{ {
hasCCRateExtension = true;
cc_enable = true; cc_enable = true;
send_rate = NormUnquantizeRate(ext.GetSendRate()); send_rate = NormUnquantizeRate(ext.GetSendRate());
// Are we in the cc_node_list? // Are we in the cc_node_list?
@ -444,6 +458,8 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
session.ActivateTimer(cc_timer); session.ActivateTimer(cc_timer);
} // end if (CC_RATE == ext.GetType()) } // end if (CC_RATE == ext.GetType())
} // end while (GetNextExtension()) } // end while (GetNextExtension())
// Disable CC feedback if sender doesn't want it
if (!hasCCRateExtension && cc_enable) cc_enable = false;
break; break;
} }
case NormCmdMsg::FLUSH: case NormCmdMsg::FLUSH:
@ -468,7 +484,9 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
if (doAck) if (doAck)
{ {
// Force sync since we're expected to ACK // Force sync since we're expected to ACK
// and request repair for object indicated
Sync(flush.GetObjectId()); Sync(flush.GetObjectId());
SetPending(flush.GetObjectId());
} }
else else
{ {
@ -1032,49 +1050,68 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (NULL != obj) if (NULL != obj)
{ {
rx_table.Insert(obj); rx_table.Insert(obj);
NormFtiExtension fti; if (129 == msg.GetFecId())
while (msg.GetNextExtension(fti))
{ {
if (NormHeaderExtension::FTI == fti.GetType())
// Look for FEC Object Transport Information header extension
NormFtiExtension fti;
while (msg.GetNextExtension(fti))
{ {
// Pre-open receive object and notify app for accept. if (NormHeaderExtension::FTI == fti.GetType())
if (obj->Open(fti.GetObjectSize(),
msg.FlagIsSet(NormObjectMsg::FLAG_INFO),
fti.GetSegmentSize(),
fti.GetFecMaxBlockLen(),
fti.GetFecNumParity()))
{ {
session.Notify(NormController::RX_OBJECT_NEW, this, obj); #ifndef ASSUME_MDP_FEC
if (obj->Accepted()) if (0 != fti.GetFecInstanceId())
{ {
// (TBD) Do I _need_ to call "StreamUpdateStatus()" here? DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC instance id\n");
if (obj->IsStream())
(static_cast<NormStreamObject*>(obj))->StreamUpdateStatus(blockId);
DMSG(8, "NormServerNode::HandleObjectMessage() node>%lu server>%lu new obj>%hu\n",
LocalNodeId(), GetId(), (UINT16)objectId);
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() object not accepted\n");
DeleteObject(obj); DeleteObject(obj);
obj = NULL; obj = NULL;
} }
else
#endif // !ASSUME_MDP_FEC
if (obj->Open(fti.GetObjectSize(),
msg.FlagIsSet(NormObjectMsg::FLAG_INFO),
fti.GetSegmentSize(),
fti.GetFecMaxBlockLen(),
fti.GetFecNumParity()))
{
session.Notify(NormController::RX_OBJECT_NEW, this, obj);
if (obj->Accepted())
{
// (TBD) Do I _need_ to call "StreamUpdateStatus()" here?
if (obj->IsStream())
(static_cast<NormStreamObject*>(obj))->StreamUpdateStatus(blockId);
DMSG(8, "NormServerNode::HandleObjectMessage() node>%lu server>%lu new obj>%hu\n",
LocalNodeId(), GetId(), (UINT16)objectId);
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() object not accepted\n");
DeleteObject(obj);
obj = NULL;
}
}
else
{
DMSG(0, "NormServerNode::HandleObjectMessage() error opening object\n");
DeleteObject(obj);
obj = NULL;
}
break;
} }
else }
{ if ((NULL != obj) && !obj->IsOpen())
DMSG(0, "NormServerNode::HandleObjectMessage() error opening object\n"); {
DeleteObject(obj); DMSG(0, "NormServerNode::HandleObjectMessage() node>%lu server>%lu "
obj = NULL; "new obj>%hu - no FTI provided!\n", LocalNodeId(), GetId(), (UINT16)objectId);
} DeleteObject(obj);
break; obj = NULL;
} }
} }
if (obj && !obj->IsOpen()) else
{ {
DMSG(0, "NormServerNode::HandleObjectMessage() node>%lu server>%lu " DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC type\n");
"new obj>%hu - no FTI provided!\n", LocalNodeId(), GetId(), (UINT16)objectId);
DeleteObject(obj); DeleteObject(obj);
obj = NULL; obj = NULL;
} }
} }
break; break;
@ -1090,7 +1127,14 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (obj) if (obj)
{ {
obj->HandleObjectMessage(msg, msgType, blockId, segmentId); 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 // Reliable reception of this object has completed
if (NormObject::FILE == obj->GetType()) if (NormObject::FILE == obj->GetType())
@ -1102,7 +1146,7 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
if (NormObject::STREAM != obj->GetType()) if (NormObject::STREAM != obj->GetType())
{ {
// Streams never complete unless they are "closed" by sender // 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); session.Notify(NormController::RX_OBJECT_COMPLETED, this, obj);
DeleteObject(obj); DeleteObject(obj);
obj = NULL; obj = NULL;
@ -1145,6 +1189,21 @@ bool NormServerNode::SyncTest(const NormObjectMsg& msg) const
} // end NormServerNode::SyncTest() } // 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" // This method establishes the sync point "sync_id"
// objectId. The sync point is the first ordinal // objectId. The sync point is the first ordinal
@ -1176,9 +1235,7 @@ void NormServerNode::Sync(NormObjectId objectId)
NormObject* obj; NormObject* obj;
while ((obj = rx_table.Find(rx_table.RangeLo()))) while ((obj = rx_table.Find(rx_table.RangeLo())))
{ {
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj); AbortObject(obj);
DeleteObject(obj);
failure_count++;
} }
rx_pending_mask.Clear(); rx_pending_mask.Clear();
} }
@ -1188,9 +1245,7 @@ void NormServerNode::Sync(NormObjectId objectId)
while ((obj = rx_table.Find(rx_table.RangeLo())) && while ((obj = rx_table.Find(rx_table.RangeLo())) &&
(obj->GetId() < objectId)) (obj->GetId() < objectId))
{ {
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj); AbortObject(obj);
DeleteObject(obj);
failure_count++;
} }
unsigned long numBits = (UINT16)(objectId - firstPending) + 1; unsigned long numBits = (UINT16)(objectId - firstPending) + 1;
rx_pending_mask.UnsetBits(firstPending, numBits); 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? //if (slow_start) // (TBD) should we only set flag on actual slow_start?
ext.SetCCFlag(NormCC::START); ext.SetCCFlag(NormCC::START);
ext.SetCCRate(NormQuantizeRate(2.0 * recv_rate)); 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 else
{ {
@ -1568,7 +1626,10 @@ bool NormServerNode::OnRepairTimeout(ProtoTimer& /*theTimer*/)
} }
if (NormRepairRequest::INVALID != nextForm) if (NormRepairRequest::INVALID != nextForm)
{ {
nack->AttachRepairRequest(req, segment_size); // (TBD) error check if (0 != segment_size)
nack->AttachRepairRequest(req, segment_size); // (TBD) error check
else
nack->AttachRepairRequest(req, NormNackMsg::DEFAULT_LENGTH_MAX);
req.SetForm(nextForm); req.SetForm(nextForm);
req.ResetFlags(); req.ResetFlags();
// Set flags for missing objects according to // Set flags for missing objects according to
@ -2413,7 +2474,7 @@ NormLossEstimator2::NormLossEstimator2()
event_window(0), event_index(0), event_window(0), event_index(0),
event_window_time(0.0), event_index_time(0.0), event_window_time(0.0), event_index_time(0.0),
seeking_loss_event(true), 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) current_discount(1.0)
{ {
memset(history, 0, 9*sizeof(unsigned long)); memset(history, 0, 9*sizeof(unsigned long));
@ -2527,11 +2588,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
if (seeking_loss_event) 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 (outageDepth) // non-zero outageDepth means pkt loss(es)
{ {
if (no_loss) // first loss if (no_loss) // first loss
@ -2547,11 +2603,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
no_loss = false; 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 method
// New loss event, shift loss interval history & discounts // 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)); (((double)currentTime.tv_usec)/1.0e06));
event_index_time += event_window_time; 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 else
{ {
@ -2595,20 +2633,9 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
return newLossEvent; return newLossEvent;
} // end NormLossEstimator2::Update() } // 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 // TFRC Loss interval averaging with discounted, weighted averaging
double NormLossEstimator2::TfrcLossFraction() double NormLossEstimator2::LossFraction()
{ {
if (!history[1]) return 0.0; if (!history[1]) return 0.0;
// Compute older weighted average s1->s8 for discount determination // Compute older weighted average s1->s8 for discount determination

View File

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

View File

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

View File

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

View File

@ -19,10 +19,6 @@ class NormSegmentPool
void Put(char* segment) void Put(char* segment)
{ {
ASSERT(seg_count < seg_total); 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 *((char**)segment) = seg_list; // this might make a warning on Solaris
seg_list = segment; seg_list = segment;
seg_count++; seg_count++;
@ -32,7 +28,6 @@ class NormSegmentPool
unsigned long CurrentUsage() const {return (seg_total - seg_count);} unsigned long CurrentUsage() const {return (seg_total - seg_count);}
unsigned long PeakUsage() const {return peak_usage;} unsigned long PeakUsage() const {return peak_usage;}
unsigned long OverunCount() const {return overruns;} unsigned long OverunCount() const {return overruns;}
unsigned int GetSegmentSize() {return seg_size;} unsigned int GetSegmentSize() {return seg_size;}
private: private:

View File

@ -13,16 +13,21 @@ const double NormSession::DEFAULT_BACKOFF_FACTOR = 4.0;
const double NormSession::DEFAULT_GSIZE_ESTIMATE = 1000.0; const double NormSession::DEFAULT_GSIZE_ESTIMATE = 1000.0;
const UINT16 NormSession::DEFAULT_NDATA = 64; const UINT16 NormSession::DEFAULT_NDATA = 64;
const UINT16 NormSession::DEFAULT_NPARITY = 32; 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) NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
: session_mgr(sessionMgr), notify_pending(false), tx_port(0), : 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), rx_socket(ProtoSocket::UDP), local_node_id(localNodeId),
ttl(DEFAULT_TTL), tos(0), loopback(false), rx_port_reuse(false), rx_addr_bind(false), 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), 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), backoff_factor(DEFAULT_BACKOFF_FACTOR), is_server(false), instance_id(0),
ndata(DEFAULT_NDATA), nparity(DEFAULT_NPARITY), auto_parity(0), extra_parity(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), tx_cache_size_max((UINT32)20*1024*1024),
flush_count(NORM_ROBUST_FACTOR+1), flush_count(NORM_ROBUST_FACTOR+1),
posted_tx_queue_empty(false), posted_tx_queue_empty(false),
@ -36,7 +41,8 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY), grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY),
grtt_response(false), grtt_current_peak(0.0), grtt_age(0.0), 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), 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_repair_boundary(NormServerNode::BLOCK_BOUNDARY),
default_nacking_mode(NormObject::NACK_NORMAL), default_nacking_mode(NormObject::NACK_NORMAL),
trace(false), tx_loss_rate(0.0), rx_loss_rate(0.0), 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); report_timer.SetRepeat(-1);
} }
NormSession::~NormSession() NormSession::~NormSession()
{ {
Close(); Close();
@ -229,9 +236,8 @@ void NormSession::Close()
bool NormSession::SetMulticastInterface(const char* interfaceName) bool NormSession::SetMulticastInterface(const char* interfaceName)
{ {
if (interfaceName) if (NULL != interfaceName)
{ {
bool result = true; bool result = true;
if (rx_socket.IsOpen()) if (rx_socket.IsOpen())
result &= rx_socket.SetMulticastInterface(interfaceName); result &= rx_socket.SetMulticastInterface(interfaceName);
@ -358,19 +364,19 @@ bool NormSession::StartServer(UINT16 instanceId,
if (!Open(interfaceName)) return false; if (!Open(interfaceName)) return false;
} }
// (TBD) parameterize the object history depth // (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"); DMSG(0, "NormSession::StartServer() tx_table.Init() error!\n");
StopServer(); StopServer();
return false; 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"); DMSG(0, "NormSession::StartServer() tx_pending_mask.Init() error!\n");
StopServer(); StopServer();
return false; 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"); DMSG(0, "NormSession::StartServer() tx_repair_mask.Init() error!\n");
StopServer(); StopServer();
@ -407,7 +413,15 @@ bool NormSession::StartServer(UINT16 instanceId,
if (numParity) 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"); DMSG(0, "NormSession::StartServer() encoder init error\n");
StopServer(); StopServer();
@ -477,7 +491,12 @@ void NormSession::StopServer()
repair_timer.Deactivate(); repair_timer.Deactivate();
tx_repair_pending = false; tx_repair_pending = false;
} }
encoder.Destroy(); if (NULL != encoder)
{
encoder->Destroy();
delete encoder;
encoder = NULL;
}
acking_node_tree.Destroy(); acking_node_tree.Destroy();
cc_node_list.Destroy(); cc_node_list.Destroy();
// Iterate tx_table and release objects // 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()) if (!posted_tx_queue_empty && stream->IsPending() && !stream->IsClosing())
{ {
//data_active = false; //data_active = false;
@ -756,7 +776,7 @@ void NormSession::Serve()
Notify(NormController::TX_QUEUE_EMPTY, (NormServerNode*)NULL, obj); Notify(NormController::TX_QUEUE_EMPTY, (NormServerNode*)NULL, obj);
// (TBD) Was session deleted? // (TBD) Was session deleted?
return; return;
} }
} }
else else
{ {
@ -778,9 +798,7 @@ void NormSession::Serve()
{ {
data_active = false; // (TBD) should we wait until the flush process completes before setting false??? data_active = false; // (TBD) should we wait until the flush process completes before setting false???
posted_tx_queue_empty = true; posted_tx_queue_empty = true;
Notify(NormController::TX_QUEUE_EMPTY, Notify(NormController::TX_QUEUE_EMPTY, (NormServerNode*)NULL, (NormObject*)NULL);
(NormServerNode*)NULL,
(NormObject*)NULL);
// (TBD) Was session deleted? // (TBD) Was session deleted?
return; return;
} }
@ -1103,7 +1121,8 @@ void NormSession::QueueMessage(NormMsg* msg)
tx_timer.SetInterval(0.0); tx_timer.SetInterval(0.0);
ActivateTimer(tx_timer); ActivateTimer(tx_timer);
} }
if (msg) message_queue.Append(msg); if (msg)
message_queue.Append(msg);
} // end NormSesssion::QueueMessage(NormMsg& msg) } // end NormSesssion::QueueMessage(NormMsg& msg)
@ -1339,29 +1358,52 @@ void NormSession::DeleteTxObject(NormObject* obj)
obj->Release(); obj->Release();
} // end NormSession::DeleteTxObject() } // end NormSession::DeleteTxObject()
void NormSession::SetTxCacheBounds(NormObjectSize sizeMax, bool NormSession::SetTxCacheBounds(NormObjectSize sizeMax,
unsigned long countMin, unsigned long countMin,
unsigned long countMax) unsigned long countMax)
{ {
bool result = true;
tx_cache_size_max = sizeMax; tx_cache_size_max = sizeMax;
tx_cache_count_min = (countMin < countMax) ? countMin : countMax; tx_cache_count_min = (countMin < countMax) ? countMin : countMax;
tx_cache_count_max = (countMax > countMin) ? countMax : countMin; tx_cache_count_max = (countMax > countMin) ? countMax : countMin;
// Now trim the tx_table as needed
if (IsServer()) if (IsServer())
{ {
// Trim/resize the tx_table and tx masks as needed
unsigned long count = tx_table.Count(); unsigned long count = tx_table.Count();
while ((count >= tx_cache_count_min) && while ((count >= tx_cache_count_min) &&
((count >= tx_cache_count_max) || ((count >= tx_cache_count_max) ||
(tx_table.GetSize() > tx_cache_size_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()); NormObject* oldest = tx_table.Find(tx_table.RangeLo());
ASSERT(oldest); ASSERT(oldest);
Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, oldest); Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, oldest);
DeleteTxObject(oldest); DeleteTxObject(oldest);
count = tx_table.Count(); 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() } // end NormSession::SetTxCacheBounds()
NormBlock* NormSession::ServerGetFreeBlock(NormObjectId objectId, NormBlock* NormSession::ServerGetFreeBlock(NormObjectId objectId,
@ -1550,14 +1592,10 @@ void NormTrace(const struct timeval& currentTime,
if (data.IsData() && data.IsStream()) if (data.IsData() && data.IsStream())
{ {
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_MSG_START)) //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 ... DMSG(0, "start word>%hu ", msgStartOffset - 1);
// 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));
} }
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_STREAM_END)) //if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_STREAM_END))
if (0 == NormDataMsg::ReadStreamPayloadLength(data.GetPayload())) if (0 == NormDataMsg::ReadStreamPayloadLength(data.GetPayload()))
@ -1923,13 +1961,11 @@ void NormSession::ServerHandleCCFeedback(struct timeval currentTime,
NormCCNode* node = (NormCCNode*)cc_node_list.FindNodeById(nodeId); NormCCNode* node = (NormCCNode*)cc_node_list.FindNodeById(nodeId);
if (node) ccRtt = node->UpdateRtt(ccRtt); if (node) ccRtt = node->UpdateRtt(ccRtt);
if (0 == (ccFlags & NormCC::START)) bool ccSlowStart = (0 != (ccFlags & NormCC::START));
{
// slow start has ended if (!ccSlowStart)
cc_slow_start = false; ccRate = CalculateRate(nominal_packet_size, ccRtt, ccLoss);
// adjust rate using current rtt for node
ccRate = CalculateRate(nominal_packet_size, ccRtt, ccLoss);
}
//DMSG(0, "NormSession::ServerHandleCCFeedback() node>%lu rate>%lf (rtt>%lf loss>%lf slow_start>%d)\n", //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))); // 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->SetCCSequence(ccSequence);
next->SetActive(true); next->SetActive(true);
next->SetFeedbackTime(currentTime); next->SetFeedbackTime(currentTime);
cc_slow_start = ccSlowStart; // use CLR status for our slow_start state
if (savedId == nodeId) if (savedId == nodeId)
{ {
// This was feedback from the current CLR // This was feedback from the current CLR
@ -2559,6 +2596,7 @@ void NormSession::ServerHandleNackMessage(const struct timeval& currentTime, Nor
} // end if (freshBlock) } // end if (freshBlock)
ASSERT(NULL != block); ASSERT(NULL != block);
// If stream && explicit data repair, lock the data for retransmission // 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)) if (object->IsStream() && (nextSegmentId < ndata))
{ {
bool attemptLock = true; bool attemptLock = true;
@ -3017,7 +3055,7 @@ bool NormSession::OnRepairTimeout(ProtoTimer& /*theTimer*/)
if (!tx_pending_mask.Set(objectId)) if (!tx_pending_mask.Set(objectId))
DMSG(0, "NormSession::OnRepairTimeout() rx_pending_mask.Set(%hu) error (2)\n", DMSG(0, "NormSession::OnRepairTimeout() rx_pending_mask.Set(%hu) error (2)\n",
(UINT16)objectId); (UINT16)objectId);
} }
} }
} // end while (iterator.GetNextObject()) } // end while (iterator.GetNextObject())
PromptServer(); PromptServer();
@ -3103,9 +3141,18 @@ bool NormSession::OnTxTimeout(ProtoTimer& /*theTimer*/)
} }
else 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 // Requeue the message for another try
if (!advertise_repairs) if (!advertise_repairs)
message_queue.Prepend(msg); 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 return true; // reinstall tx_timer
} }
@ -3203,12 +3250,17 @@ bool NormSession::SendMessage(NormMsg& msg)
msg.SetSourceId(local_node_id); msg.SetSourceId(local_node_id);
UINT16 msgSize = msg.GetLength(); UINT16 msgSize = msg.GetLength();
bool result = true;
// Possibly drop some tx messages for testing purposes // Possibly drop some tx messages for testing purposes
bool drop = (UniformRand(100.0) < tx_loss_rate); bool drop = (UniformRand(100.0) < tx_loss_rate);
if (drop || (isClientMsg && client_silent)) if (drop || (isClientMsg && client_silent))
{ {
//DMSG(0, "TX MESSAGE DROPPED! (tx_loss_rate:%lf\n", tx_loss_rate); //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 else
{ {
@ -3228,40 +3280,34 @@ bool NormSession::SendMessage(NormMsg& msg)
} }
else else
{ {
DMSG(8, "NormSession::SendMessage() sendto() error\n"); DMSG(8, "NormSession::SendMessage() sendto() error: %s\n", GetErrorString());
result = false; tx_sequence--;
return false;
} }
} }
if (result) if (isProbe)
{ {
if (isProbe) probe_pending = false;
probe_data_check = true;
if (probe_reset)
{ {
probe_pending = false; probe_reset = false;
probe_data_check = true; if (!probe_timer.IsActive())
if (probe_reset) ActivateTimer(probe_timer);
{
probe_reset = false;
if (!probe_timer.IsActive())
ActivateTimer(probe_timer);
}
} }
else if (!isClientMsg)
{
probe_data_check = false;
if (!probe_pending && probe_reset)
{
probe_reset = false;
OnProbeTimeout(probe_timer);
if (!probe_timer.IsActive())
ActivateTimer(probe_timer);
}
}
return true;
} }
else else if (!isClientMsg)
{ {
return false; probe_data_check = false;
if (!probe_pending && probe_reset)
{
probe_reset = false;
OnProbeTimeout(probe_timer);
if (!probe_timer.IsActive())
ActivateTimer(probe_timer);
}
} }
return true;
} // end NormSession::SendMessage() } // end NormSession::SendMessage()
void NormSession::SetGrttProbingInterval(double intervalMin, double intervalMax) 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)); feedbackAge += 1.0e-06*((double)(currentTime.tv_usec - feedbackTime.tv_usec));
else else
feedbackAge -= 1.0e-06*((double)(feedbackTime.tv_usec - currentTime.tv_usec)); 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 // Safety bound to compensate for computer clock coarseness
// and possible sluggish feedback from slower machines // and possible sluggish feedback from slower machines
// at higher norm data rates (keeps rate from being // at higher norm data rates (keeps rate from being
@ -3511,7 +3557,11 @@ bool NormSession::OnProbeTimeout(ProtoTimer& /*theTimer*/)
if (maxFeedbackAge <(10*NORM_TICK_MIN)) if (maxFeedbackAge <(10*NORM_TICK_MIN))
maxFeedbackAge = (10*NORM_TICK_MIN); maxFeedbackAge = (10*NORM_TICK_MIN);
if (feedbackAge > maxFeedbackAge) if (feedbackAge > maxFeedbackAge)
{
DMSG(6, "Deactivating cc node feedbackAge:%lf sec maxAge:%lf sec\n",
feedbackAge, maxFeedbackAge);
next->SetActive(false); next->SetActive(false);
}
} }
} }

View File

@ -101,6 +101,9 @@ class NormSession
static const double DEFAULT_GSIZE_ESTIMATE; static const double DEFAULT_GSIZE_ESTIMATE;
static const UINT16 DEFAULT_NDATA; static const UINT16 DEFAULT_NDATA;
static const UINT16 DEFAULT_NPARITY; 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 ProbingMode {PROBE_NONE, PROBE_PASSIVE, PROBE_ACTIVE};
enum AckingStatus enum AckingStatus
{ {
@ -129,7 +132,7 @@ class NormSession
// (TBD) call tx_socket->SetFlowLabel() to set traffic class for IPv6 sockets // (TBD) call tx_socket->SetFlowLabel() to set traffic class for IPv6 sockets
// (or should we have ProtoSocket::SetTOS() do this for us?) // (or should we have ProtoSocket::SetTOS() do this for us?)
bool result = tx_socket->IsOpen() ? tx_socket->SetTOS(theTOS) : true; bool result = tx_socket->IsOpen() ? tx_socket->SetTOS(theTOS) : true;
tos = result ? theTOS : ttl; tos = result ? theTOS : tos;
return result; return result;
} }
bool SetLoopback(bool state) bool SetLoopback(bool state)
@ -177,7 +180,7 @@ class NormSession
void SetGrttMax(double grttMax) {grtt_max = grttMax;} void SetGrttMax(double grttMax) {grtt_max = grttMax;}
void SetTxRateBounds(double rateMin, double rateMax); void SetTxRateBounds(double rateMin, double rateMax);
void SetTxCacheBounds(NormObjectSize sizeMax, bool SetTxCacheBounds(NormObjectSize sizeMax,
unsigned long countMin, unsigned long countMin,
unsigned long countMax); unsigned long countMax);
void Notify(NormController::Event event, void Notify(NormController::Event event,
@ -247,6 +250,13 @@ class NormSession
void ServerSetExtraParity(UINT16 extraParity) void ServerSetExtraParity(UINT16 extraParity)
{extra_parity = 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) bool ServerGetFirstPending(NormObjectId& objectId)
{ {
UINT32 index; UINT32 index;
@ -282,7 +292,7 @@ class NormSession
} }
void ServerEncode(const char* segment, char** parityVectorList) void ServerEncode(const char* segment, char** parityVectorList)
{encoder.Encode(segment, parityVectorList);} {encoder->Encode(segment, parityVectorList);}
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId); NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
@ -313,16 +323,30 @@ class NormSession
{return remote_server_buffer_size;} {return remote_server_buffer_size;}
void ClientSetUnicastNacks(bool state) {unicast_nacks = state;} void ClientSetUnicastNacks(bool state) {unicast_nacks = state;}
bool ClientGetUnicastNacks() const {return unicast_nacks;} bool ClientGetUnicastNacks() const {return unicast_nacks;}
void ClientSetSilent(bool state) void ClientSetSilent(bool state)
{ {client_silent = state;}
client_silent = state;
receiver_low_delay = client_silent ? receiver_low_delay : false;
}
bool ClientIsSilent() const {return client_silent;} bool ClientIsSilent() const {return client_silent;}
void ReceiverSetLowDelay(bool state) void RcvrSetIgnoreInfo(bool state)
{receiver_low_delay = client_silent ? state : false;} {rcvr_ignore_info = state;}
bool ReceiverIsLowDelay() {return receiver_low_delay;} 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 NormObject::NackingMode ClientGetDefaultNackingMode() const
{return default_nacking_mode;} {return default_nacking_mode;}
@ -401,8 +425,8 @@ class NormSession
bool notify_pending; bool notify_pending;
ProtoTimer tx_timer; ProtoTimer tx_timer;
UINT16 tx_port; UINT16 tx_port;
ProtoSocket* tx_socket;
ProtoSocket tx_socket_actual; ProtoSocket tx_socket_actual;
ProtoSocket* tx_socket;
ProtoSocket rx_socket; ProtoSocket rx_socket;
NormMessageQueue message_queue; NormMessageQueue message_queue;
NormMessageQueue message_pool; NormMessageQueue message_pool;
@ -431,6 +455,7 @@ class NormSession
UINT16 nparity; UINT16 nparity;
UINT16 auto_parity; UINT16 auto_parity;
UINT16 extra_parity; UINT16 extra_parity;
bool sndr_emcon;
NormObjectTable tx_table; NormObjectTable tx_table;
ProtoSlidingMask tx_pending_mask; ProtoSlidingMask tx_pending_mask;
@ -438,7 +463,7 @@ class NormSession
ProtoTimer repair_timer; ProtoTimer repair_timer;
NormBlockPool block_pool; NormBlockPool block_pool;
NormSegmentPool segment_pool; NormSegmentPool segment_pool;
NormEncoder encoder; NormEncoder* encoder;
NormObjectId next_tx_object_id; NormObjectId next_tx_object_id;
unsigned int tx_cache_count_min; unsigned int tx_cache_count_min;
@ -507,7 +532,8 @@ class NormSession
unsigned long remote_server_buffer_size; unsigned long remote_server_buffer_size;
bool unicast_nacks; bool unicast_nacks;
bool client_silent; bool client_silent;
bool receiver_low_delay; bool rcvr_ignore_info;
INT32 rcvr_max_delay;
NormServerNode::RepairBoundary default_repair_boundary; NormServerNode::RepairBoundary default_repair_boundary;
NormObject::NackingMode default_nacking_mode; NormObject::NackingMode default_nacking_mode;

View File

@ -1,5 +1,4 @@
#include "normSimAgent.h" #include "normSimAgent.h"
#include <errno.h> #include <errno.h>
// This NORM simulation agent includes support for providing transport // This NORM simulation agent includes support for providing transport
@ -8,22 +7,22 @@
NormSimAgent::NormSimAgent(ProtoTimerMgr& timerMgr, NormSimAgent::NormSimAgent(ProtoTimerMgr& timerMgr,
ProtoSocket::Notifier& socketNotifier) ProtoSocket::Notifier& socketNotifier)
: session_mgr(timerMgr, socketNotifier), session(NULL), : msg_sink(NULL), session_mgr(timerMgr, socketNotifier), session(NULL),
address(NULL), port(0), ttl(3), address(NULL), port(0), ttl(3),
tx_rate(NormSession::DEFAULT_TRANSMIT_RATE), tx_rate(NormSession::DEFAULT_TRANSMIT_RATE),
cc_enable(false), unicast_nacks(false), silent_client(false), cc_enable(false), unicast_nacks(false), silent_client(false),
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR), backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0), segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0),
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE), group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE), grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE),
tx_buffer_size(1024*1024), rx_buffer_size(1024*1024), tx_buffer_size(1024*1024), rx_buffer_size(1024*1024),
tx_object_size(0), tx_object_interval(0.0), tx_object_size(0), tx_object_interval(0.0),
tx_object_size_min(0), tx_object_size_max(0), tx_object_size_min(0), tx_object_size_max(0),
tx_repeat_count(0), tx_repeat_interval(0.0), tx_repeat_count(0), tx_repeat_interval(0.0),
stream(NULL), auto_stream(false), push_mode(false), stream(NULL), auto_stream(false), push_mode(false),
flush_mode(NormStreamObject::FLUSH_PASSIVE), 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) tracing(false), tx_loss(0.0), rx_loss(0.0)
{ {
// Bind NormSessionMgr to this agent and simulation environment // Bind NormSessionMgr to this agent and simulation environment
session_mgr.SetController(static_cast<NormController*>(this)); session_mgr.SetController(static_cast<NormController*>(this));
@ -586,6 +585,8 @@ bool NormSimAgent::SendMessage(unsigned int len, const char* txBuffer)
DMSG(0, "NormSimAgent::SendMessage() session not started!\n"); DMSG(0, "NormSimAgent::SendMessage() session not started!\n");
return false; return false;
} }
return true;
} // end NormSimAgent::SendMessage() } // end NormSimAgent::SendMessage()
void NormSimAgent::OnInputReady() void NormSimAgent::OnInputReady()
@ -622,246 +623,242 @@ bool NormSimAgent::FlushStream()
void NormSimAgent::Notify(NormController::Event event, void NormSimAgent::Notify(NormController::Event event,
class NormSessionMgr* sessionMgr, class NormSessionMgr* sessionMgr,
class NormSession* session, class NormSession* session,
class NormServerNode* server, class NormServerNode* server,
class NormObject* object) class NormObject* object)
{ {
switch (event) switch (event)
{ {
//case TX_QUEUE_VACANCY: //case TX_QUEUE_VACANCY:
case TX_QUEUE_EMPTY: case TX_QUEUE_EMPTY:
// Can queue a new object or write to stream for transmission // Can queue a new object or write to stream for transmission
if (object && (object == stream)) if (object && (object == stream))
{
if (auto_stream)
{
// sending a dummy byte stream
char buffer[NormMsg::MAX_SIZE];
stream->Write(buffer, segment_size, false);
}
else
{
// Stream starved, ask for input from "source" ?
OnInputReady();
}
}
else
{
// Schedule or queue next "sim file" transmission
if (interval_timer.GetInterval() > 0.0)
{
ActivateTimer(interval_timer);
}
else
{
OnIntervalTimeout(interval_timer);
}
}
break;
case RX_OBJECT_NEW:
{
// It's up to the app to "accept" the object
switch (object->GetType())
{
case NormObject::STREAM:
{ {
if (auto_stream) NormObjectSize size;
{ if (silent_client)
// sending a dummy byte stream size = NormObjectSize(rx_buffer_size);
char buffer[NormMsg::MAX_SIZE];
stream->Write(buffer, segment_size, false);
}
else else
size = object->GetSize();
if (!((NormStreamObject*)object)->Accept(size.LSB()))
{ {
// Stream starved, ask for input from "source" ? DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) stream object accept error!\n");
OnInputReady(); }
if (!stream)
stream = (NormStreamObject*)object;
else
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) warning! one stream already accepted.\n");
}
break;
case NormObject::FILE:
{
if (!((NormSimObject*)object)->Accept())
{
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) sim object accept error!\n");
} }
} }
else
{
// Schedule or queue next "sim file" transmission
if (interval_timer.GetInterval() > 0.0)
{
ActivateTimer(interval_timer);
}
else
{
OnIntervalTimeout(interval_timer);
}
}
break; break;
case NormObject::DATA:
case RX_OBJECT_NEW: DMSG(0, "NormSimAgent::Notify() DATA objects not _yet_ supported...\n");
break;
default:
DMSG(0, "NormSimAgent::Notify() INVALID object type!\n");
ASSERT(0);
break;
}
break;
}
case RX_OBJECT_INFO:
switch(object->GetType())
{
case NormObject::FILE:
case NormObject::DATA:
case NormObject::STREAM:
default:
break;
} // end switch(object->GetType())
break;
case RX_OBJECT_UPDATED:
switch (object->GetType())
{
case NormObject::FILE:
// (TBD) update progress
break;
case NormObject::STREAM:
{ {
// It's up to the app to "accept" the object // Read the stream when it's updated
switch (object->GetType()) if (msg_sink)
{ {
case NormObject::STREAM: bool dataReady = true;
{ while (dataReady)
NormObjectSize size; {
if (silent_client) if (!mgen_pending_bytes)
size = NormObjectSize(rx_buffer_size);
else
size = object->GetSize();
if (!((NormStreamObject*)object)->Accept(size.LSB()))
{ {
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) stream object accept error!\n"); // Read 2 byte MGEN msg len header
} unsigned int want = 2 - mgen_bytes;
if (!stream)
stream = (NormStreamObject*)object;
else
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) warning! one stream already accepted.\n");
}
break;
case NormObject::FILE:
{
if (!((NormSimObject*)object)->Accept())
{
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) sim object accept error!\n");
}
}
break;
case NormObject::DATA:
DMSG(0, "NormSimAgent::Notify() DATA objects not _yet_ supported...\n");
break;
default:
DMSG(0, "NormSimAgent::Notify() INVALID object type!\n");
ASSERT(0);
break;
}
break;
}
case RX_OBJECT_INFO:
switch(object->GetType())
{
case NormObject::FILE:
case NormObject::DATA:
case NormObject::STREAM:
default:
break;
} // end switch(object->GetType())
break;
case RX_OBJECT_UPDATED:
switch (object->GetType())
{
case NormObject::FILE:
// (TBD) update progress
break;
case NormObject::STREAM:
{
// Read the stream when it's updated
if (mgen)
{
bool dataReady = true;
while (dataReady)
{
if (!mgen_pending_bytes)
{
// Read 2 byte MGEN msg len header
unsigned int want = 2 - mgen_bytes;
unsigned int got = want;
bool findMsgSync = msg_sync ? false : true;
if ((static_cast<NormStreamObject*>(object))->Read(mgen_buffer + mgen_bytes,
&got, findMsgSync))
{
mgen_bytes += got;
msg_sync = true;
if (got != want) dataReady = false;
}
else
{
DMSG(0, "NormSimAgent::Notify(1) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
continue;
}
if (2 == mgen_bytes)
{
UINT16 msgSize;
memcpy(&msgSize, mgen_buffer, sizeof(UINT16));
mgen_pending_bytes = ntohs(msgSize) - 2;
}
}
if (mgen_pending_bytes)
{
// Save the first part for MGEN logging
if (mgen_bytes < 64)
{
unsigned int want = MIN(mgen_pending_bytes, 62);
unsigned int got = want;
if ((static_cast<NormStreamObject*>(object))->Read(mgen_buffer+mgen_bytes,
&got))
{
mgen_pending_bytes -= got;
mgen_bytes += got;
if (got != want) dataReady = false;
}
else
{
DMSG(0, "NormSimAgent::Notify(2) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
continue;
}
}
while (dataReady && mgen_pending_bytes)
{
char buffer[256];
unsigned int want = MIN(256, mgen_pending_bytes);
unsigned int got = want;
if ((static_cast<NormStreamObject*>(object))->Read(buffer, &got))
{
mgen_pending_bytes -= got;
mgen_bytes += got;
if (got != want) dataReady = false;
}
else
{
DMSG(0, "NormSimAgent::Notify(3) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
break;
}
}
if (msg_sync && (0 == mgen_pending_bytes))
{
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
mgen_bytes = 0;
}
} // end if (mgen_pending_bytes)
} // end while(dataReady)
}
else
{
char buffer[1024];
unsigned int want = 1024;
unsigned int got = want; unsigned int got = want;
while (1) bool findMsgSync = msg_sync ? false : true;
if ((static_cast<NormStreamObject*>(object))->Read(mgen_buffer + mgen_bytes,
&got, findMsgSync))
{ {
if ((static_cast<NormStreamObject*>(object))->Read(buffer, &got)) mgen_bytes += got;
msg_sync = true;
if (got != want) dataReady = false;
}
else
{
DMSG(0, "NormSimAgent::Notify(1) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
continue;
}
if (2 == mgen_bytes)
{
UINT16 msgSize;
memcpy(&msgSize, mgen_buffer, sizeof(UINT16));
mgen_pending_bytes = ntohs(msgSize) - 2;
}
}
if (mgen_pending_bytes)
{
// Save the first part for MGEN logging
if (mgen_bytes < 64)
{
unsigned int want = MIN(mgen_pending_bytes, 62);
unsigned int got = want;
if ((static_cast<NormStreamObject*>(object))->Read(mgen_buffer+mgen_bytes,
&got))
{ {
// Break when data is no longer available mgen_pending_bytes -= got;
if (got != want) break; mgen_bytes += got;
if (got != want) dataReady = false;
} }
else else
{ {
DMSG(0, "NormSimAgent::Notify() detected stream break\n"); DMSG(0, "NormSimAgent::Notify(2) detected stream break\n");
mgen_bytes = mgen_pending_bytes = 0;
msg_sync = false;
continue;
} }
got = want = 1024;
} }
} while (dataReady && mgen_pending_bytes)
break; {
} char buffer[256];
unsigned int want = MIN(256, mgen_pending_bytes);
case NormObject::DATA: unsigned int got = want;
DMSG(0, "NormSimAgent::Notify() FILE/DATA objects not _yet_ supported...\n"); if ((static_cast<NormStreamObject*>(object))->Read(buffer, &got))
break; {
mgen_pending_bytes -= got;
default: mgen_bytes += got;
// should never occur if (got != want) dataReady = false;
break; }
} // end switch (object->GetType()) else
break; {
case RX_OBJECT_COMPLETED: DMSG(0, "NormSimAgent::Notify(3) detected stream break\n");
{ mgen_bytes = mgen_pending_bytes = 0;
switch(object->GetType()) msg_sync = false;
break;
}
}
if (msg_sync && (0 == mgen_pending_bytes))
{
ProtoAddress srcAddr;
srcAddr.ResolveFromString(server->GetAddress().GetHostString());
srcAddr.SetPort(server->GetAddress().GetPort());
msg_sink->HandleMessage(mgen_buffer,mgen_bytes,srcAddr);
mgen_bytes = 0;
}
} // end if (mgen_pending_bytes)
} // end while(dataReady)
}
else
{ {
case NormObject::FILE: char buffer[1024];
//DMSG(0, "norm: Completed rx file: %s\n", ((NormFileObject*)object)->Path()); unsigned int want = 1024;
break; unsigned int got = want;
case NormObject::STREAM: while (1)
//DMSG(0, "norm: Completed rx stream ...\n"); {
break; if ((static_cast<NormStreamObject*>(object))->Read(buffer, &got))
case NormObject::DATA: {
ASSERT(0); // Break when data is no longer available
break; if (got != want) break;
default: }
break; else
{
DMSG(0, "NormSimAgent::Notify() detected stream break\n");
}
got = want = 1024;
}
} }
break; break;
} }
default:
DMSG(0, "NormSimAgent::Notify() unhandled NormEvent type\n"); case NormObject::DATA:
DMSG(0, "NormSimAgent::Notify() FILE/DATA objects not _yet_ supported...\n");
break;
default:
// should never occur
break;
} // end switch (object->GetType())
break;
case RX_OBJECT_COMPLETED:
{
switch(object->GetType())
{
case NormObject::FILE:
//DMSG(0, "norm: Completed rx file: %s\n", ((NormFileObject*)object)->Path());
break;
case NormObject::STREAM:
//DMSG(0, "norm: Completed rx stream ...\n");
break;
case NormObject::DATA:
ASSERT(0);
break;
default:
break;
}
break;
}
default:
DMSG(0, "NormSimAgent::Notify() unhandled NormEvent type\n");
} // end switch(event) } // end switch(event)
} // end NormSimAgent::Notify() } // end NormSimAgent::Notify()

View File

@ -1,13 +1,14 @@
#ifndef _NORM_SIM_AGENT
#define _NORM_SIM_AGENT
// normSimAgent.h - Generic (base class) NORM simulation agent // normSimAgent.h - Generic (base class) NORM simulation agent
#include "normSession.h" #include "normSession.h"
#include "protokit.h" #include "protokit.h"
#include "protoSimAgent.h"
#include "mgen.h" // for MGEN instance attachment
// Base class for Norm simulation agents (e.g. ns-2, OPNET, etc) // Base class for Norm simulation agents (e.g. ns-2, OPNET, etc)
class NormSimAgent : public NormController class NormSimAgent : public NormController, public ProtoMessageSink
{ {
public: public:
virtual ~NormSimAgent(); virtual ~NormSimAgent();
@ -20,8 +21,8 @@ class NormSimAgent : public NormController
bool IsActive() {return (NULL != session);} bool IsActive() {return (NULL != session);}
void Stop(); void Stop();
bool SendMessage(unsigned int len, const char* txBuffer);
void AttachMgen(Mgen* mgenInstance) {mgen = mgenInstance;} bool SendMessage(unsigned int len, const char* txBuffer);
protected: protected:
NormSimAgent(ProtoTimerMgr& timerMgr, NormSimAgent(ProtoTimerMgr& timerMgr,
@ -29,11 +30,15 @@ class NormSimAgent : public NormController
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG}; enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
CmdType CommandType(const char* cmd); CmdType CommandType(const char* cmd);
virtual unsigned long GetAgentId() = 0; virtual unsigned long GetAgentId() = 0;
/* JPH 4/11/06 Use packet stream instead of direct call on mgen process */ ProtoMessageSink* msg_sink;
void HandleMgenMessage(char* buffer,
unsigned int len, #ifdef OPNET
const ProtoAddress& srcAddr); void HandleMessage(char* buffer,
unsigned int len,
const ProtoAddress& srcAddr);
void SetSink(ProtoMessageSink* sink){msg_sink=sink;}
#endif //OPNET
private: private:
void OnInputReady(); void OnInputReady();
bool FlushStream(); bool FlushStream();
@ -87,12 +92,10 @@ class NormSimAgent : public NormController
char* tx_msg_buffer; char* tx_msg_buffer;
unsigned int tx_msg_len; unsigned int tx_msg_len;
unsigned int tx_msg_index; unsigned int tx_msg_index;
Mgen* mgen; // mgen receiver
char mgen_buffer[64]; char mgen_buffer[64];
bool msg_sync; bool msg_sync;
unsigned int mgen_bytes; unsigned int mgen_bytes;
unsigned int mgen_pending_bytes; unsigned int mgen_pending_bytes;
ProtoTimer interval_timer; ProtoTimer interval_timer;
// protocol debug parameters // protocol debug parameters
@ -102,3 +105,4 @@ class NormSimAgent : public NormController
}; // end class NormSimAgent }; // 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 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()) // Uncomment to enable TCP-friendly congestion control (overrides NormSetTransmitRate())
//NormSetCongestionControl(session, true); //NormSetCongestionControl(session, true);
@ -114,7 +114,7 @@ int main(int argc, char* argv[])
//NormSetSilentReceiver(session, true); //NormSetSilentReceiver(session, true);
// Uncomment this line to participate as a receiver // Uncomment this line to participate as a receiver
NormStartReceiver(session, 1024*1024); //NormStartReceiver(session, 1024*1024);
// Uncomment to set large rx socket buffer size // Uncomment to set large rx socket buffer size
// (might be needed for high rate sessions) // (might be needed for high rate sessions)
@ -124,7 +124,7 @@ int main(int argc, char* argv[])
NormSessionId sessionId = (NormSessionId)rand(); NormSessionId sessionId = (NormSessionId)rand();
// Uncomment the following line to start sender // 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); //NormSetAutoParity(session, 6);
@ -146,7 +146,7 @@ int main(int argc, char* argv[])
// Uncomment this line to send a stream instead of the file // 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 // NORM_FLUSH_PASSIVE automatically flushes full writes to
// the stream. // 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 #ifndef _NORM_VERSION
#define _NORM_VERSION #define _NORM_VERSION
#define VERSION "1.3b9" #define VERSION "1.4b2"
#endif // _NORM_VERSION #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 * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: * modification, are permitted provided that:
@ -33,8 +33,8 @@
#include "ip.h" // for hdr_ip def #include "ip.h" // for hdr_ip def
#include "flags.h" // for hdr_flags def #include "flags.h" // for hdr_flags def
#include "nsMgenAgent.h"
#include "nsNormAgent.h" #include "nsNormAgent.h"
static class NsNormAgentClass : public TclClass static class NsNormAgentClass : public TclClass
{ {
public: public:
@ -45,7 +45,7 @@ static class NsNormAgentClass : public TclClass
NsNormAgent::NsNormAgent() NsNormAgent::NsNormAgent()
: NormSimAgent(GetTimerMgr(), GetSocketNotifier()) : NormSimAgent(GetTimerMgr(), GetSocketNotifier())
{ {
} }
@ -56,6 +56,7 @@ NsNormAgent::~NsNormAgent()
bool NsNormAgent::OnStartup(int argc, const char*const* argv) bool NsNormAgent::OnStartup(int argc, const char*const* argv)
{ {
if (ProcessCommands(argc, argv)) if (ProcessCommands(argc, argv))
{ {
return true; return true;
@ -89,10 +90,10 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
return false; return false;
} }
Tcl& tcl = Tcl::instance(); Tcl& tcl = Tcl::instance();
NsMgenAgent* mgenAgent = dynamic_cast<NsMgenAgent*> (tcl.lookup(argv[i])); msg_sink = dynamic_cast<ProtoMessageSink*>(tcl.lookup(argv[i]));
if (mgenAgent)
if (msg_sink)
{ {
AttachMgen(mgenAgent->GetMgenInstance());
i++; i++;
continue; continue;
} }
@ -145,13 +146,3 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
return true; return true;
} // end NsNormAgent::ProcessCommands() } // 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,31 +36,31 @@
#include "nsProtoSimAgent.h" // from Protolib #include "nsProtoSimAgent.h" // from Protolib
#include "normSimAgent.h" #include "normSimAgent.h"
#include "nsMgenAgent.h"
// The "NsProtoAgent" is based on the ns Agent class. // The "NsProtoAgent" is based on the ns Agent class.
// This lets us have a send/recv attachment to the NS // This lets us have a send/recv attachment to the NS
// simulation environment // simulation environment
// IMPORTANT NOTE! NsProtoAgent must be listed _first_ here // IMPORTANT NOTE! NsProtoAgent must be listed _first_ here
// (because we can't dynamic_cast install_data void* pointers) // (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: public:
NsNormAgent(); NsNormAgent();
~NsNormAgent(); ~NsNormAgent();
// NsProtoSimAgent base class overrides // NsProtoSimAgent base class overrides
virtual bool OnStartup(int argc, const char*const* argv); virtual bool OnStartup(int argc, const char*const* argv);
virtual bool ProcessCommands(int argc, const char*const* argv); virtual bool ProcessCommands(int argc, const char*const* argv);
virtual void OnShutdown(); virtual void OnShutdown();
// NormSimAgent override // NormSimAgent overrides
unsigned long GetAgentId() {return (unsigned long)addr();} unsigned long GetAgentId() {return (unsigned long)addr();}
// MgenSink override bool HandleMessage(const char* txBuffer, unsigned int len, const ProtoAddress& srcAddr)
bool SendMgenMessage(const char* txBuffer, {
unsigned int len, return NormSimAgent::SendMessage(len,txBuffer);
const ProtoAddress& dstAddr); }
}; // end class NsNormAgent }; // end class NsNormAgent

View File

@ -1,10 +1,10 @@
/* Process model C++ form file: norm_protolib.pr.cpp */ /* 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 */ /* 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> #include <string.h>
@ -116,7 +116,7 @@ class norm_protolib_state
IpT_Address dest_ip_addr ; IpT_Address dest_ip_addr ;
FILE* script_fp ; FILE* script_fp ;
int source ; int source ;
Ici* app_ici_v3_ptr ; Ici* app_ici_ptr ;
/* FSM code */ /* FSM code */
void norm_protolib (OP_SIM_CONTEXT_ARG_OPT); 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 dest_ip_addr pr_state_ptr->dest_ip_addr
#define script_fp pr_state_ptr->script_fp #define script_fp pr_state_ptr->script_fp
#define source pr_state_ptr->source #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 */ /* These macro definitions will define a local variable called */
/* "op_sv_ptr" in each function containing a FIN statement. */ /* "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_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_ptr, "local_port", local_port);
op_ici_attr_set(app_ici_v3_ptr, "rem_port", srcAddr.GetPort()); op_ici_attr_set(app_ici_ptr, "rem_port", srcAddr.GetPort());
op_ici_attr_set(app_ici_v3_ptr, "rem_addr", srcAddr.SimGetAddress()); op_ici_attr_set(app_ici_ptr, "rem_addr", srcAddr.SimGetAddress());
op_ici_attr_set(app_ici_v3_ptr, "src_addr", srcAddr.SimGetAddress()); op_ici_attr_set(app_ici_ptr, "src_addr", srcAddr.SimGetAddress());
op_ici_install(app_ici_v3_ptr); op_ici_install(app_ici_ptr);
op_pk_send_forced(pkt, OUTSTRM_TO_MGEN); op_pk_send_forced(pkt, OUTSTRM_TO_MGEN);
@ -851,7 +851,7 @@ void NormSimAgent::HandleMgenMessage(char* buffer,
#undef dest_ip_addr #undef dest_ip_addr
#undef script_fp #undef script_fp
#undef source #undef source
#undef app_ici_v3_ptr #undef app_ici_ptr
/* Access from C kernel using C linkage */ /* Access from C kernel using C linkage */
extern "C" 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_STATE_ENTER_UNFORCED_NOLABEL (1, "init", "norm_protolib [init enter execs]")
FSM_PROFILE_SECTION_IN ("norm_protolib [init enter execs]", state1_enter_exec) 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. */ /* Obtain the object ID of the surrounding norm processor. */
my_id = op_id_self (); 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); *var_p_ptr = (void *) (&prs_ptr->source);
FOUT 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 FOUT
} }
*var_p_ptr = (void *)OPC_NIL; *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

@ -80,7 +80,27 @@ TEST_OBJ = $(TEST_SRC:.cpp=.o)
normTest: $(TEST_OBJ) libnorm.a $(LIBPROTO) normTest: $(TEST_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(TEST_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS) $(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) command-line reliable tunnel helper
RAFT_SRC = $(COMMON)/raft.cpp RAFT_SRC = $(COMMON)/raft.cpp
RAFT_OBJ = $(RAFT_SRC:.cpp=.o) RAFT_OBJ = $(RAFT_SRC:.cpp=.o)

View File

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

View File

@ -45,7 +45,7 @@ SYSTEM_SRC =
SYSTEM = macosx SYSTEM = macosx
CC = g++ CC = g++
SYSTEM_CFLAGS = -fPIC -Wall -pedantic -Wcast-align SYSTEM_CFLAGS = -fPIC -Wall -Wcast-align
SYSTEM_SOFLAGS = -dynamiclib SYSTEM_SOFLAGS = -dynamiclib
RANLIB = ranlib RANLIB = ranlib
AR = ar 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} {50D1D52B-702A-49FE-B490-9104FC72598D} = {50D1D52B-702A-49FE-B490-9104FC72598D}
EndProjectSection EndProjectSection
EndProject 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 Global
GlobalSection(SolutionConfiguration) = preSolution GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug Debug = Debug
@ -50,6 +56,10 @@ Global
{182006F3-188F-466E-89FE-8421C0478691}.Debug.Build.0 = Debug|Win32 {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.ActiveCfg = Release|Win32
{182006F3-188F-466E-89FE-8421C0478691}.Release.Build.0 = 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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection EndGlobalSection

Binary file not shown.

View File

@ -88,7 +88,7 @@
Optimization="2" Optimization="2"
InlineFunctionExpansion="1" InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\common;..\protolib\common;..\protolib\win32;NormDll3" 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" StringPooling="TRUE"
RuntimeLibrary="2" RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE" 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.