v1.4b1
parent
276f7c4e20
commit
28c6631d92
File diff suppressed because it is too large
Load Diff
Binary file not shown.
13
TODO.TXT
13
TODO.TXT
|
|
@ -55,9 +55,20 @@
|
|||
|
||||
19) Fix "activity_timer" interval setting (COMPLETED)
|
||||
|
||||
20) Double-check watermark check code around line 600 of normSession.cpp
|
||||
20) Fix issue related to setting tx cache count_max > 256!
|
||||
Fix NormSession::SetTxCacheBounds() ... tx_table may need to be
|
||||
resized accordingly (COMPLETED, but needs testing).
|
||||
|
||||
21) Double-check watermark check code around line 600 of normSession.cpp
|
||||
(COMPLETED - reimplemented, adding tx_repair_pending index to use
|
||||
instead of seeking each time)
|
||||
|
||||
22) Implement LDPC FEC code within NORM as alternative to Reed Solomon
|
||||
|
||||
23) Add ability to control receiver cache on a per-sender basis?
|
||||
(max_pending_range, etc)
|
||||
|
||||
24) Look at NormStreamObject::StreamAdvance() for "push-enabled" streams
|
||||
(COMPLETED)
|
||||
|
||||
|
||||
|
|
|
|||
25
VERSION.TXT
25
VERSION.TXT
|
|
@ -1,5 +1,30 @@
|
|||
NORM Version History
|
||||
|
||||
Version 1.4b1
|
||||
=============
|
||||
- Fixed condition where receivers kept state for remote senders "stuck"
|
||||
with CC feedback (cc_enable) even if newer NORM_CMD(CC) messages
|
||||
indicate that sender CC operation has been disabled.
|
||||
- Fixed error in NormSession::SetTOS() method that transmogrifried the
|
||||
default TTL value into TOS under certain conditions.
|
||||
(thanks to Ron in 't Velt yet again for finding this and the one above).
|
||||
- Made NormFile read/write more persistent (e.g. when EINT occurs)
|
||||
- Fixed NormObject::PassiveRepairCheck() bug that incorrectly reported
|
||||
no repair needed when in fact, the last segment was missing.
|
||||
- Fixed ProtoSlidingBitmask error that interfered with proper
|
||||
sender stream advancement under severe conditions
|
||||
- NORM still used old "MDP" FEC codecs but now explicitly check for
|
||||
FEC ID == 129 and FEC instance id == 0. Can be built with
|
||||
-DASSUME_MDP_FEC to avoid this check in case of backward compatbility
|
||||
issue. Future NORM release will be using new FEC implementation
|
||||
for Reed-Solomon coding but will continue to support compile-time
|
||||
backwards compatibility option to use old MDP FEC encoder.
|
||||
- Added "lowDelay" and other options to "norm" demo application
|
||||
- Modified "lowDelay" option for silent receiver use to allow
|
||||
control of FEC block delay (how many blocks to wait before passing
|
||||
partially-received coding blocks to application).
|
||||
- Added "Norm Pre-Coder" (NPC) utility to distribution.
|
||||
|
||||
Version 1.3b9
|
||||
=============
|
||||
- Fixed ProtoSocket WINVER stuff and NormApplicationMsg::SetContent()
|
||||
|
|
|
|||
|
|
@ -491,9 +491,14 @@ bool NormInstance::GetNextEvent(NormEvent* theEvent)
|
|||
}
|
||||
break;
|
||||
case NORM_RX_OBJECT_UPDATED:
|
||||
// reset update event notification
|
||||
((NormObject*)n->event.object)->SetNotifyOnUpdate(true);
|
||||
{
|
||||
// reset update event notification for non-streams
|
||||
// (NormStreamRead() takes care of streams)
|
||||
NormObject* obj = ((NormObject*)n->event.object);
|
||||
if (!obj->IsStream())
|
||||
obj->SetNotifyOnUpdate(true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1502,13 +1507,13 @@ bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle,
|
|||
NORM_API_LINKAGE
|
||||
void NormSetSilentReceiver(NormSessionHandle sessionHandle,
|
||||
bool silent,
|
||||
bool lowDelay)
|
||||
INT32 maxDelay)
|
||||
{
|
||||
NormSession* session = (NormSession*)sessionHandle;
|
||||
if (session)
|
||||
{
|
||||
session->ClientSetSilent(silent);
|
||||
session->ReceiverSetLowDelay(lowDelay);
|
||||
session->RcvrSetMaxDelay(maxDelay);
|
||||
}
|
||||
} // end NormSetSilentReceiver()
|
||||
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ bool NormSetRxSocketBuffer(NormSessionHandle sessionHandle,
|
|||
NORM_API_LINKAGE
|
||||
void NormSetSilentReceiver(NormSessionHandle sessionHandle,
|
||||
bool silent,
|
||||
bool lowDelay = false);
|
||||
int maxDelay = -1);
|
||||
|
||||
NORM_API_LINKAGE
|
||||
void NormSetDefaultUnicastNack(NormSessionHandle sessionHandle,
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ class NormApp : public NormController, public ProtoApp
|
|||
bool loopback;
|
||||
char* interface_name; // for multi-home hosts
|
||||
double tx_rate; // bits/sec
|
||||
double tx_rate_min;
|
||||
double tx_rate_max;
|
||||
bool cc_enable;
|
||||
|
||||
// NormSession server-only parameters
|
||||
|
|
@ -107,6 +109,13 @@ class NormApp : public NormController, public ProtoApp
|
|||
unsigned long tx_buffer_size; // bytes
|
||||
NormFileList tx_file_list;
|
||||
bool tx_one_shot;
|
||||
bool tx_ack_shot;
|
||||
bool tx_file_queued;
|
||||
|
||||
// save last obj/block/seg id for later in case needed
|
||||
NormObjectId tx_last_object_id;
|
||||
NormBlockId tx_last_block_id;
|
||||
NormSegmentId tx_last_segment_id;
|
||||
double tx_object_interval;
|
||||
int tx_repeat_count;
|
||||
double tx_repeat_interval;
|
||||
|
|
@ -123,6 +132,8 @@ class NormApp : public NormController, public ProtoApp
|
|||
NormPostProcessor* post_processor;
|
||||
bool unicast_nacks;
|
||||
bool silent_client;
|
||||
bool low_delay;
|
||||
bool process_aborted_files;
|
||||
|
||||
// Debug parameters
|
||||
bool tracing;
|
||||
|
|
@ -140,16 +151,17 @@ NormApp::NormApp()
|
|||
input_messaging(false), input_msg_length(0), input_msg_index(0),
|
||||
output_index(0), output_messaging(false), output_msg_length(0), output_msg_sync(false),
|
||||
address(NULL), port(0), ttl(32), loopback(false), interface_name(NULL),
|
||||
tx_rate(64000.0), cc_enable(false),
|
||||
tx_rate(64000.0), tx_rate_min(-1.0), tx_rate_max(-1.0), cc_enable(false),
|
||||
segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0),
|
||||
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
|
||||
grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE),
|
||||
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
|
||||
tx_buffer_size(1024*1024), tx_one_shot(false),
|
||||
tx_buffer_size(1024*1024), tx_one_shot(false), tx_ack_shot(false), tx_file_queued(false),
|
||||
tx_object_interval(0.0), tx_repeat_count(0), tx_repeat_interval(2.0), tx_repeat_clear(true),
|
||||
acking_node_list(NULL), watermark_pending(false),
|
||||
rx_buffer_size(1024*1024), rx_sock_buffer_size(0),
|
||||
rx_cache_path(NULL), post_processor(NULL), unicast_nacks(false), silent_client(false),
|
||||
low_delay(false), process_aborted_files(false),
|
||||
tracing(false), tx_loss(0.0), rx_loss(0.0)
|
||||
{
|
||||
|
||||
|
|
@ -209,6 +221,7 @@ const char* const NormApp::cmd_list[] =
|
|||
"+interface", // multicast interface name to use
|
||||
"+cc", // congestion control on/off
|
||||
"+rate", // tx date rate (bps)
|
||||
"+limit", // tx rate limits <rateMin:rateMax>
|
||||
"-push", // push stream writes for real-time messaging
|
||||
"+flush", // message flushing mode ("none", "passive", or "active")
|
||||
"+input", // send stream input
|
||||
|
|
@ -220,6 +233,7 @@ const char* const NormApp::cmd_list[] =
|
|||
"+repeatcount", // How many times to repeat the file/directory list tx
|
||||
"+rinterval", // Interval (sec) between file/directory list repeats
|
||||
"-oneshot", // Transmit file(s), exiting upon TX_FLUSH_COMPLETED
|
||||
"-ackshot", // Transmit file(s), exiting upon TX_WATERMARK_COMPLETED
|
||||
"-updatesOnly", // only send updated files on repeat transmission
|
||||
"+ackingNodes", // comma-delimited list of node id's to from which to collect acks
|
||||
"+rxcachedir", // recv file cache directory
|
||||
|
|
@ -235,7 +249,9 @@ const char* const NormApp::cmd_list[] =
|
|||
"+rxbuffer", // Size receiver allocates for buffering each sender
|
||||
"+rxsockbuffer", // Optional recv socket buffer size.
|
||||
"-unicastNacks", // unicast instead of multicast feedback messages
|
||||
"-silentClient", // "silent" (non-nacking) client (EMCON mode)
|
||||
"-silentClient", // "silent" (non-nacking) receiver (EMCON mode) (must set for sender too)
|
||||
"-lowDelay", // for silent receivers only, delivers data/files to app sooner\n"
|
||||
"-saveAborts", // save (and possibly post-process) aborted receive files\n"
|
||||
"+processor", // receive file post processing command
|
||||
"+instance", // specify norm instance name for remote control commands
|
||||
NULL
|
||||
|
|
@ -261,6 +277,7 @@ void NormApp::ShowHelp()
|
|||
" +interface, // multicast interface name to use\n"
|
||||
" +cc, // congestion control 'on' or 'off'\n"
|
||||
" +rate, // tx date rate (bps)\n"
|
||||
" +limit // tx rate bounds <rateMin:rateMax>\n"
|
||||
" -push, // push stream writes for real-time messaging\n"
|
||||
" +flush, // message flushing mode ('none', 'passive', or 'active')\n"
|
||||
" +input, // send stream input\n"
|
||||
|
|
@ -272,6 +289,7 @@ void NormApp::ShowHelp()
|
|||
" +repeatcount, // How many times to repeat the file/directory list tx\n"
|
||||
" +rinterval, // Interval (sec) between file/directory list repeats\n"
|
||||
" -oneshot, // Exit upon sender TX_FLUSH_COMPLETED event (sender exits after transmission)\n"
|
||||
" -ackshot, // Exit upon sender TX_WATERMARK_COMPLETED event (sender exits after transmission)\n"
|
||||
" -updatesOnly, // only send updated files on repeat transmission\n"
|
||||
" +ackingNodes, // comma-delimited list of node id's to from which to collect acks\n"
|
||||
" +rxcachedir, // recv file cache directory\n"
|
||||
|
|
@ -287,7 +305,9 @@ void NormApp::ShowHelp()
|
|||
" +rxbuffer, // Size receiver allocates for buffering each sender\n"
|
||||
" +rxsockbuffer, // Optional recv socket buffer size.\n"
|
||||
" -unicastNacks, // unicast instead of multicast feedback messages\n"
|
||||
" -silentClient, // silent (non-nacking) receiver (EMCON mode)\n"
|
||||
" -silentClient, // silent (non-nacking) receiver (EMCON mode) (must set for sender too)\n"
|
||||
" -lowDelay, // for silent receivers only, delivers data/files to app sooner\n"
|
||||
" -saveAborts, // save (and possibly post-process) aborted receive files\n"
|
||||
" +processor, // receive file post processing command\n"
|
||||
" +instance, // specify norm instance name for remote control commands\n"
|
||||
"\n");
|
||||
|
|
@ -527,6 +547,18 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
|
|||
tx_rate = txRate;
|
||||
if (session) session->SetTxRate(txRate);
|
||||
}
|
||||
else if (!strncmp("rate", cmd, len))
|
||||
{
|
||||
double rateMin, rateMax;
|
||||
if (2 != sscanf(val, "%lf:%lf", &rateMin, &rateMax))
|
||||
{
|
||||
DMSG(0, "NormApp::OnCommand(rate) invalid txRate limits!\n");
|
||||
return false;
|
||||
}
|
||||
tx_rate_min = rateMin;
|
||||
tx_rate_max = rateMax;
|
||||
if (session) session->SetTxRateBounds(rateMin, rateMax);
|
||||
}
|
||||
else if (!strncmp("cc", cmd, len))
|
||||
{
|
||||
if (!strcmp("on", val))
|
||||
|
|
@ -657,6 +689,11 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
|
|||
{
|
||||
tx_one_shot = true;
|
||||
}
|
||||
|
||||
else if (!strncmp("ackshot", cmd, len))
|
||||
{
|
||||
tx_ack_shot = true;
|
||||
}
|
||||
else if (!strncmp("updatesOnly", cmd, len))
|
||||
{
|
||||
tx_file_list.InitUpdateTime(true);
|
||||
|
|
@ -823,7 +860,20 @@ bool NormApp::OnCommand(const char* cmd, const char* val)
|
|||
else if (!strncmp("silentClient", cmd, len))
|
||||
{
|
||||
silent_client = true;
|
||||
if (session) session->ClientSetSilent(true);
|
||||
if (session)
|
||||
{
|
||||
session->ClientSetSilent(true);
|
||||
session->SndrSetEmcon(true);
|
||||
}
|
||||
}
|
||||
else if (!strncmp("lowDelay", cmd, len))
|
||||
{
|
||||
low_delay = true;
|
||||
if (session) session->RcvrSetMaxDelay(1);
|
||||
}
|
||||
else if (!strncmp("saveAborts", cmd, len))
|
||||
{
|
||||
process_aborted_files = true;
|
||||
}
|
||||
else if (!strncmp("push", cmd, len))
|
||||
{
|
||||
|
|
@ -1189,6 +1239,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
switch (event)
|
||||
{
|
||||
case TX_QUEUE_VACANCY:
|
||||
DMSG(6, "NormApp::Notify(TX_QUEUE_VACANCY) ...\n");
|
||||
if (NULL != object)
|
||||
{
|
||||
if (input && (object == tx_stream))
|
||||
|
|
@ -1197,7 +1248,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
break;
|
||||
case TX_QUEUE_EMPTY:
|
||||
// Write to stream as needed
|
||||
DMSG(3, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
|
||||
DMSG(6, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
|
||||
if (NULL != object)
|
||||
{
|
||||
if (input && (object == tx_stream))
|
||||
|
|
@ -1215,11 +1266,11 @@ void NormApp::Notify(NormController::Event event,
|
|||
break;
|
||||
|
||||
case TX_OBJECT_SENT:
|
||||
DMSG(3, "NormApp::Notify(TX_OBJECT_SENT) ...\n");
|
||||
DMSG(4, "NormApp::Notify(TX_OBJECT_SENT) ...\n");
|
||||
break;
|
||||
|
||||
case TX_FLUSH_COMPLETED:
|
||||
DMSG(3, "NormApp::Notify(TX_FLUSH_COMPLETED) ...\n");
|
||||
DMSG(4, "NormApp::Notify(TX_FLUSH_COMPLETED) ...\n");
|
||||
if (tx_one_shot)
|
||||
{
|
||||
DMSG(0, "norm: transmit flushing completed, exiting.\n");
|
||||
|
|
@ -1229,18 +1280,27 @@ void NormApp::Notify(NormController::Event event,
|
|||
|
||||
case TX_WATERMARK_COMPLETED:
|
||||
{
|
||||
DMSG(3, "NormApp::Notify(TX_WATERMARK_COMPLETED) ...\n");
|
||||
NormSession::AckingStatus status = session->ServerGetAckingStatus(NORM_NODE_ANY);
|
||||
|
||||
DMSG(0, "norm: positive ack collection completed (%s)\n",
|
||||
(NormSession::ACK_SUCCESS == status) ? "succeed" : "failed");
|
||||
DMSG(4, "NormApp::Notify(TX_WATERMARK_COMPLETED) ...\n");
|
||||
//NormSession::AckingStatus status = session->ServerGetAckingStatus(NORM_NODE_ANY);
|
||||
watermark_pending = false; // enable new watermark to be set
|
||||
if (tx_ack_shot)
|
||||
{
|
||||
DMSG(0, "norm: transmit flushing completed, exiting.\n");
|
||||
Stop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RX_OBJECT_NEW:
|
||||
{
|
||||
DMSG(3, "NormApp::Notify(RX_OBJECT_NEW) ...\n");
|
||||
DMSG(4, "NormApp::Notify(RX_OBJECT_NEW) ...\n");
|
||||
struct timeval currentTime;
|
||||
ProtoSystemTime(currentTime);
|
||||
struct tm* timePtr = gmtime((time_t*)¤tTime.tv_sec);
|
||||
DMSG(3, "%02d:%02d:%02d.%06lu start rx object>%hu sender>%lu\n",
|
||||
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
|
||||
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
|
||||
|
||||
// It's up to the app to "accept" the object
|
||||
switch (object->GetType())
|
||||
{
|
||||
|
|
@ -1342,7 +1402,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
}
|
||||
|
||||
case RX_OBJECT_INFO:
|
||||
DMSG(3, "NormApp::Notify(RX_OBJECT_INFO) ...\n");
|
||||
DMSG(4, "NormApp::Notify(RX_OBJECT_INFO) ...\n");
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
|
|
@ -1383,7 +1443,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
break;
|
||||
|
||||
case RX_OBJECT_UPDATED:
|
||||
DMSG(3, "NormApp::Notify(RX_OBJECT_UPDATED) ...\n");
|
||||
DMSG(6, "NormApp::Notify(RX_OBJECT_UPDATED) ...\n");
|
||||
switch (object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
|
|
@ -1433,7 +1493,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
}
|
||||
|
||||
if(!((NormStreamObject*)object)->Read(output_buffer+output_index,
|
||||
&readLength, findMsgSync))
|
||||
&readLength, findMsgSync))
|
||||
{
|
||||
// The stream broke
|
||||
if (output_messaging)
|
||||
|
|
@ -1442,7 +1502,10 @@ void NormApp::Notify(NormController::Event event,
|
|||
DMSG(0, "NormApp::Notify() detected broken stream ...\n");
|
||||
output_msg_length = output_index = 0;
|
||||
output_msg_sync = false;
|
||||
break;
|
||||
if (findMsgSync)
|
||||
break;
|
||||
else
|
||||
continue; // try to re-sync
|
||||
}
|
||||
}
|
||||
else if (readLength > 0)
|
||||
|
|
@ -1479,6 +1542,7 @@ void NormApp::Notify(NormController::Event event,
|
|||
while (put < writeLength)
|
||||
{
|
||||
size_t result = fwrite(output_buffer+put, sizeof(char), writeLength-put, output);
|
||||
|
||||
if (result)
|
||||
{
|
||||
put += (unsigned int)result;
|
||||
|
|
@ -1526,7 +1590,13 @@ void NormApp::Notify(NormController::Event event,
|
|||
// (TBD) if we're not archiving files we should
|
||||
// manage our cache, deleting the cache
|
||||
// on shutdown ...
|
||||
DMSG(3, "NormApp::Notify(RX_OBJECT_COMPLETED) ...\n");
|
||||
DMSG(4, "NormApp::Notify(RX_OBJECT_COMPLETED) ...\n");
|
||||
struct timeval currentTime;
|
||||
ProtoSystemTime(currentTime);
|
||||
struct tm* timePtr = gmtime((time_t*)¤tTime.tv_sec);
|
||||
DMSG(3, "%02d:%02d:%02d.%06lu completed rx object>%hu sender>%lu\n",
|
||||
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
|
||||
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
|
|
@ -1554,6 +1624,45 @@ void NormApp::Notify(NormController::Event event,
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RX_OBJECT_ABORTED:
|
||||
{
|
||||
DMSG(0, "NormApp::Notify(RX_OBJECT_ABORTED) ...\n");
|
||||
struct timeval currentTime;
|
||||
ProtoSystemTime(currentTime);
|
||||
struct tm* timePtr = gmtime((time_t*)¤tTime.tv_sec);
|
||||
DMSG(3, "%02d:%02d:%02d.%06lu aborted rx object>%hu sender>%lu\n",
|
||||
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
|
||||
(unsigned long)currentTime.tv_usec, (UINT16)object->GetId(), server->GetId());
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
{
|
||||
const char* filePath = static_cast<NormFileObject*>(object)->GetPath();
|
||||
if (process_aborted_files)
|
||||
{
|
||||
// in case file size isn't padded properly
|
||||
static_cast<NormFileObject*>(object)->PadToSize();
|
||||
if (post_processor->IsEnabled())
|
||||
{
|
||||
if (!post_processor->ProcessFile(filePath))
|
||||
{
|
||||
DMSG(0, "norm: post processing error\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NormFile::Unlink(filePath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
DMSG(4, "NormApp::Notify() unhandled event: %d\n", event);
|
||||
|
|
@ -1600,13 +1709,25 @@ bool NormApp::OnIntervalTimeout(ProtoTimer& /*theTimer*/)
|
|||
ActivateTimer(interval_timer);
|
||||
return false;
|
||||
}
|
||||
if (!watermark_pending && (NULL != acking_node_list))
|
||||
tx_file_queued = true;
|
||||
|
||||
|
||||
struct timeval currentTime;
|
||||
ProtoSystemTime(currentTime);
|
||||
struct tm* timePtr = gmtime((time_t*)¤tTime.tv_sec);
|
||||
DMSG(3, "%02d:%02d:%02d.%06lu enqueued tx object>%hu sender>%lu\n",
|
||||
timePtr->tm_hour, timePtr->tm_min, timePtr->tm_sec,
|
||||
(unsigned long)currentTime.tv_usec, (UINT16)obj->GetId(), session->LocalNodeId());
|
||||
|
||||
// save last obj/block/seg id for later in case needed
|
||||
tx_last_object_id = obj->GetId();
|
||||
tx_last_block_id = obj->GetFinalBlockId();
|
||||
tx_last_segment_id = obj->GetBlockSize(tx_last_block_id) - 1;
|
||||
if (!watermark_pending && (NULL != acking_node_list) && !tx_ack_shot)
|
||||
{
|
||||
NormBlockId blockId = obj->GetFinalBlockId();
|
||||
NormSegmentId segmentId = obj->GetBlockSize(blockId) - 1;
|
||||
session->ServerSetWatermark(obj->GetId(),
|
||||
blockId,
|
||||
segmentId);
|
||||
session->ServerSetWatermark(tx_last_object_id,
|
||||
tx_last_block_id,
|
||||
tx_last_segment_id);
|
||||
watermark_pending = true; // only allow one pending watermark at a time
|
||||
}
|
||||
//DMSG(0, "norm: File \"%s\" queued for transmission.\n", fileName);
|
||||
|
|
@ -1645,6 +1766,13 @@ bool NormApp::OnIntervalTimeout(ProtoTimer& /*theTimer*/)
|
|||
{
|
||||
tx_repeat_clear = true;
|
||||
DMSG(0, "norm: End of tx file list reached.\n");
|
||||
if (tx_ack_shot && !watermark_pending && tx_file_queued)
|
||||
{
|
||||
session->ServerSetWatermark(tx_last_object_id,
|
||||
tx_last_block_id,
|
||||
tx_last_segment_id);
|
||||
watermark_pending = true; // only allow one pending watermark at a time
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormApp::OnIntervalTimeout()
|
||||
|
|
@ -1695,6 +1823,7 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
|
|||
// Common session parameters
|
||||
session->SetTxPort(tx_port);
|
||||
session->SetTxRate(tx_rate);
|
||||
session->SetTxRateBounds(tx_rate_min, tx_rate_max);
|
||||
session->SetTrace(tracing);
|
||||
session->SetTxLoss(tx_loss);
|
||||
session->SetRxLoss(rx_loss);
|
||||
|
|
@ -1715,7 +1844,8 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
|
|||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
// redundant info transmission if we have silent clients
|
||||
if (silent_client) session->SndrSetEmcon(true);
|
||||
// We also use the baseId as our server's "instance id" for illustrative purposes
|
||||
UINT16 instanceId = baseId;
|
||||
if (!session->StartServer(instanceId, tx_buffer_size, segment_size, ndata, nparity, interface_name))
|
||||
|
|
@ -1746,6 +1876,10 @@ bool NormApp::OnStartup(int argc, const char*const* argv)
|
|||
// StartClient(bufferMax (per-sender))
|
||||
session->ClientSetUnicastNacks(unicast_nacks);
|
||||
session->ClientSetSilent(silent_client);
|
||||
if (low_delay)
|
||||
session->RcvrSetMaxDelay(1);
|
||||
else
|
||||
session->RcvrSetMaxDelay(-1);
|
||||
if (!session->StartClient(rx_buffer_size, interface_name))
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() start client error!\n");
|
||||
|
|
|
|||
|
|
@ -41,23 +41,35 @@
|
|||
#include "normMessage.h"
|
||||
#endif // SIMULATE
|
||||
|
||||
NormEncoder::NormEncoder()
|
||||
#include <stdio.h>
|
||||
#include "protoDefs.h" // for struct timeval
|
||||
#define DIFF_T(a,b) (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
|
||||
|
||||
NormEncoder::~NormEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
NormDecoder::~NormDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
NormEncoderRS8a::NormEncoderRS8a()
|
||||
: npar(0), vector_size(0),
|
||||
gen_poly(NULL), scratch(NULL)
|
||||
{
|
||||
|
||||
} // end NormEncoder::NormEncoder()
|
||||
} // end NormEncoderRS8a::NormEncoderRS8a()
|
||||
|
||||
NormEncoder::~NormEncoder()
|
||||
NormEncoderRS8a::~NormEncoderRS8a()
|
||||
{
|
||||
if (gen_poly) Destroy();
|
||||
}
|
||||
|
||||
bool NormEncoder::Init(int numParity, int vecSizeMax)
|
||||
bool NormEncoderRS8a::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
|
||||
{
|
||||
// Debugging assertions
|
||||
ASSERT((numParity>=0)&&(numParity<129));
|
||||
ASSERT(vecSizeMax >= 0);
|
||||
ASSERT((numData + numParity) <= 255);
|
||||
if ((numData + numParity) > 255) return false; // (TBD) printout error message
|
||||
if (gen_poly) Destroy();
|
||||
npar = numParity;
|
||||
|
||||
|
|
@ -69,22 +81,22 @@ bool NormEncoder::Init(int numParity, int vecSizeMax)
|
|||
// Create gen_poly polynomial
|
||||
if(!CreateGeneratorPolynomial())
|
||||
{
|
||||
DMSG(0, "NormEncoder: Error creating gen_poly polynomial!\n");
|
||||
DMSG(0, "NormEncoderRS8a: Error creating gen_poly polynomial!\n");
|
||||
return false;
|
||||
}
|
||||
// Allocate scratch space for encoding
|
||||
if(!(scratch = new unsigned char[vecSizeMax]))
|
||||
{
|
||||
DMSG(0, "NormEncoder: Error allocating memory for encoder scratch space: %s\n",
|
||||
DMSG(0, "NormEncoderRS8a: Error allocating memory for encoder scratch space: %s\n",
|
||||
GetErrorString());
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // end NormEncoder::Init()
|
||||
} // end NormEncoderRS8a::Init()
|
||||
|
||||
// Free memory allocated for encoder state (Encoder must be re-inited before use)
|
||||
void NormEncoder::Destroy()
|
||||
void NormEncoderRS8a::Destroy()
|
||||
{
|
||||
if(NULL != scratch)
|
||||
{
|
||||
|
|
@ -96,10 +108,10 @@ void NormEncoder::Destroy()
|
|||
delete[] gen_poly;
|
||||
gen_poly = NULL;
|
||||
}
|
||||
} // end NormEncoder::Destroy()
|
||||
} // end NormEncoderRS8a::Destroy()
|
||||
|
||||
|
||||
bool NormEncoder::CreateGeneratorPolynomial()
|
||||
bool NormEncoderRS8a::CreateGeneratorPolynomial()
|
||||
{
|
||||
unsigned char *tp, *tp1, *tp2;
|
||||
int degree = 2*npar;
|
||||
|
|
@ -107,14 +119,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
|
|||
|
||||
if(!(gen_poly = new unsigned char[npar+1]))
|
||||
{
|
||||
DMSG(0, "NormEncoder: Error allocating memory for gen_poly polynomial: %s\n",
|
||||
DMSG(0, "NormEncoderRS8a: Error allocating memory for gen_poly polynomial: %s\n",
|
||||
GetErrorString());
|
||||
return false;
|
||||
}
|
||||
/* Allocate memory for temporary polynomial arrays */
|
||||
if(!(tp = new unsigned char[2*degree]))
|
||||
{
|
||||
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
|
||||
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
|
||||
GetErrorString());
|
||||
delete[] gen_poly;
|
||||
return false;
|
||||
|
|
@ -123,7 +135,7 @@ bool NormEncoder::CreateGeneratorPolynomial()
|
|||
{
|
||||
delete[] tp;
|
||||
delete[] gen_poly;
|
||||
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
|
||||
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
|
||||
GetErrorString());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -132,14 +144,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
|
|||
delete[] tp1;
|
||||
delete[] tp;
|
||||
delete[] gen_poly;
|
||||
DMSG(0, "NormEncoder: Error allocating memory while computing gen_poly: %s\n",
|
||||
DMSG(0, "NormEncoderRS8a: Error allocating memory while computing gen_poly: %s\n",
|
||||
GetErrorString());
|
||||
return false;
|
||||
}
|
||||
// multiply (x + a^n) for n = 1 to npar
|
||||
memset(tp1, 0, degree*sizeof(unsigned char));
|
||||
tp1[0] = 1;
|
||||
for (int n = 1; n <= npar; n++)
|
||||
for (unsigned int n = 1; n <= npar; n++)
|
||||
{
|
||||
memset(tp, 0, degree*sizeof(unsigned char));
|
||||
tp[0] = gexp(n); // set up x+a^n
|
||||
|
|
@ -149,14 +161,16 @@ bool NormEncoder::CreateGeneratorPolynomial()
|
|||
for (int i = 0; i < degree; i++)
|
||||
{
|
||||
memset(&tp2[degree], 0, degree*sizeof(unsigned char));
|
||||
int j;
|
||||
//unsigned int j;
|
||||
// Scale tp2 by p1[i]
|
||||
int j;
|
||||
for(j=0; j<degree; j++) tp2[j]=gmult(tp1[j], tp[i]);
|
||||
// Mult(shift) tp2 right by i
|
||||
for (j = (degree*2)-1; j >= i; j--) tp2[j] = tp2[j-i];
|
||||
for (j = (degree*2)-1; j >= i; j--)
|
||||
tp2[j] = tp2[j-i];
|
||||
memset(tp2, 0, i*sizeof(unsigned char));
|
||||
// Add into partial product
|
||||
for(j=0; j < (npar+1); j++) gen_poly[j] ^= tp2[j];
|
||||
for(unsigned int x=0; x < (npar+1); x++) gen_poly[x] ^= tp2[x];
|
||||
}
|
||||
memcpy(tp1, gen_poly, (npar+1)*sizeof(unsigned char));
|
||||
memset(&tp1[npar+1], 0, (2*degree)-(npar+1));
|
||||
|
|
@ -165,14 +179,14 @@ bool NormEncoder::CreateGeneratorPolynomial()
|
|||
delete[] tp1;
|
||||
delete[] tp;
|
||||
return true;
|
||||
} // end NormEncoder::CreateGeneratorPolynomial()
|
||||
} // end NormEncoderRS8a::CreateGeneratorPolynomial()
|
||||
|
||||
|
||||
|
||||
// Encode data vectors one at a time. The user of this function
|
||||
// must keep track of when parity is ready for transmission
|
||||
// Parity data is written to list of parity vectors supplied by caller
|
||||
void NormEncoder::Encode(const char *data, char **pVec)
|
||||
void NormEncoderRS8a::Encode(const char *data, char **pVec)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char *userData, *LSFR1, *LSFR2, *pVec0;
|
||||
|
|
@ -188,6 +202,7 @@ void NormEncoder::Encode(const char *data, char **pVec)
|
|||
UINT16 vecSize = vector_size;
|
||||
#endif // if/else SIMULATE
|
||||
|
||||
|
||||
memcpy(scratch, pVec[0], vecSize);
|
||||
if (npar > 1)
|
||||
{
|
||||
|
|
@ -200,38 +215,39 @@ void NormEncoder::Encode(const char *data, char **pVec)
|
|||
for(j = 0; j < vecSize; j++)
|
||||
*LSFR1++ = *LSFR2++ ^
|
||||
gmult(*genPoly, (*userData++ ^ *pVec0++));
|
||||
genPoly--;
|
||||
genPoly--;
|
||||
}
|
||||
|
||||
}
|
||||
pVec0 = scratch;
|
||||
userData = (unsigned char *) data;
|
||||
LSFR1 = (unsigned char *) pVec[npar_minus_one];
|
||||
for(j = 0; j < vecSize; j++)
|
||||
*LSFR1++ = gmult(*genPoly, (*userData++ ^ *pVec0++));
|
||||
} // end NormEncoder::Encode()
|
||||
} // end NormEncoderRS8a::Encode()
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
* NormDecoder implementation routines
|
||||
* NormDecoderRS8a implementation routines
|
||||
*/
|
||||
|
||||
NormDecoder::NormDecoder()
|
||||
NormDecoderRS8a::NormDecoderRS8a()
|
||||
: npar(0), vector_size(0), lambda(NULL),
|
||||
s_vec(NULL), o_vec(NULL), scratch(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NormDecoder::~NormDecoder()
|
||||
NormDecoderRS8a::~NormDecoderRS8a()
|
||||
{
|
||||
if (lambda) Destroy();
|
||||
if (NULL != lambda) Destroy();
|
||||
}
|
||||
|
||||
bool NormDecoder::Init(int numParity, int vecSizeMax)
|
||||
bool NormDecoderRS8a::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
|
||||
{
|
||||
// Debugging assertions
|
||||
ASSERT((numParity>=0)&&(numParity<=128));
|
||||
ASSERT(vecSizeMax >= 0);
|
||||
ASSERT((numData + numParity) <= 255);
|
||||
if ((numData + numParity) > 255) return false;
|
||||
|
||||
#ifdef SIMULATE
|
||||
vecSizeMax = MIN(SIM_PAYLOAD_MAX+1, vecSizeMax);
|
||||
|
|
@ -244,7 +260,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
|
|||
|
||||
if(!(lambda = new unsigned char[2*npar]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for lambda: %s\n",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for lambda: %s\n",
|
||||
GetErrorString());
|
||||
return(false);
|
||||
}
|
||||
|
|
@ -252,17 +268,17 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
|
|||
/* Allocate memory for s_vec ptr and the syndrome vectors */
|
||||
if(!(s_vec = new unsigned char*[npar]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for s_vec ptr: %s\n",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for s_vec ptr: %s\n",
|
||||
GetErrorString());
|
||||
Destroy();
|
||||
return(false);
|
||||
}
|
||||
int i;
|
||||
unsigned int i;
|
||||
for(i=0; i < npar; i++)
|
||||
{
|
||||
if(!(s_vec[i] = new unsigned char[vecSizeMax]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for new s_vec: %s\n",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for new s_vec: %s\n",
|
||||
GetErrorString());
|
||||
Destroy();
|
||||
return(false);
|
||||
|
|
@ -272,7 +288,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
|
|||
/* Allocate memory for the o_vec ptr and the Omega vectors */
|
||||
if(!(o_vec = new unsigned char*[npar]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for new o_vec ptr: %s\n",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for new o_vec ptr: %s\n",
|
||||
GetErrorString());
|
||||
Destroy();
|
||||
return(false);
|
||||
|
|
@ -282,7 +298,7 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
|
|||
{
|
||||
if(!(o_vec[i] = new unsigned char[vecSizeMax]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for new o_vec: %s",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for new o_vec: %s",
|
||||
GetErrorString());
|
||||
Destroy();
|
||||
return(false);
|
||||
|
|
@ -291,15 +307,15 @@ bool NormDecoder::Init(int numParity, int vecSizeMax)
|
|||
|
||||
if (!(scratch = new unsigned char[vecSizeMax]))
|
||||
{
|
||||
DMSG(0, "NormDecoder: Error allocating memory for scratch space: %s",
|
||||
DMSG(0, "NormDecoderRS8a: Error allocating memory for scratch space: %s",
|
||||
GetErrorString());
|
||||
}
|
||||
memset(scratch, 0, vecSizeMax*sizeof(unsigned char));
|
||||
return(true);
|
||||
} // end NormDecoder::Init()
|
||||
} // end NormDecoderRS8a::Init()
|
||||
|
||||
|
||||
void NormDecoder::Destroy()
|
||||
void NormDecoderRS8a::Destroy()
|
||||
{
|
||||
if (scratch)
|
||||
{
|
||||
|
|
@ -308,14 +324,14 @@ void NormDecoder::Destroy()
|
|||
}
|
||||
if(o_vec)
|
||||
{
|
||||
for(int i=0; i<npar; i++)
|
||||
for(unsigned int i=0; i<npar; i++)
|
||||
if (o_vec[i]) delete[] o_vec[i];
|
||||
delete[] o_vec;
|
||||
o_vec = NULL;
|
||||
}
|
||||
if(s_vec)
|
||||
{
|
||||
for(int i = 0; i < npar; i++)
|
||||
for(unsigned int i = 0; i < npar; i++)
|
||||
if (s_vec[i]) delete[] s_vec[i];
|
||||
delete[] s_vec;
|
||||
s_vec = NULL;
|
||||
|
|
@ -326,35 +342,35 @@ void NormDecoder::Destroy()
|
|||
delete[] lambda;
|
||||
lambda = NULL;
|
||||
}
|
||||
} // end NormDecoder::Destroy()
|
||||
} // end NormDecoderRS8a::Destroy()
|
||||
|
||||
|
||||
// This will crash & burn if (erasureCount > npar)
|
||||
int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* erasureLocs)
|
||||
int NormDecoderRS8a::Decode(char** dVec, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs)
|
||||
{
|
||||
// Debugging assertions
|
||||
ASSERT(lambda);
|
||||
ASSERT(erasureCount && (erasureCount<=npar));
|
||||
ASSERT(erasureCount && (erasureCount <= npar));
|
||||
|
||||
// (A) Compute syndrome vectors
|
||||
|
||||
// First zero out erasure vectors (MDP provides zero-filled vecs)
|
||||
// First zero out erasure vectors (NORM provides zero-filled vecs)
|
||||
|
||||
// Then calculate syndrome (based on zero value erasures)
|
||||
int nvecs = npar + ndata;
|
||||
unsigned int nvecs = npar + numData;
|
||||
#ifdef SIMULATE
|
||||
int vecSize = MIN(SIM_PAYLOAD_MAX+1, vector_size);
|
||||
UINT16 vecSize = MIN(SIM_PAYLOAD_MAX+1, vector_size);
|
||||
#else
|
||||
int vecSize = vector_size;
|
||||
UINT16 vecSize = vector_size;
|
||||
#endif // if/else SIMUATE
|
||||
|
||||
int i;
|
||||
unsigned int i;
|
||||
for (i = 0; i < npar; i++)
|
||||
{
|
||||
int X = gexp(i+1);
|
||||
unsigned char* synVec = s_vec[i];
|
||||
memset(synVec, 0, vecSize*sizeof(char));
|
||||
for(int j = 0; j < nvecs; j++)
|
||||
for(unsigned int j = 0; j < nvecs; j++)
|
||||
{
|
||||
unsigned char* data = dVec[j] ? (unsigned char*)dVec[j] : scratch;
|
||||
unsigned char* S = synVec;
|
||||
|
|
@ -367,8 +383,8 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
|
|||
}
|
||||
|
||||
// (B) Init lambda (the erasure locator polynomial)
|
||||
int degree = 2*npar;
|
||||
int nvecsMinusOne = nvecs - 1;
|
||||
unsigned int degree = 2*npar;
|
||||
unsigned int nvecsMinusOne = nvecs - 1;
|
||||
memset(lambda, 0, degree*sizeof(char));
|
||||
lambda[0] = 1;
|
||||
for (i = 0; i < erasureCount; i++)
|
||||
|
|
@ -383,13 +399,13 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
|
|||
{
|
||||
int k = i;
|
||||
memset(o_vec[i], 0, vecSize*sizeof(char));
|
||||
int m = i + 1;
|
||||
for(int j = 0; j < m; j++)
|
||||
//int m = i + 1;
|
||||
for(unsigned int j = 0; j <= i; j++)
|
||||
{
|
||||
unsigned char* Omega = o_vec[i];
|
||||
unsigned char* S = s_vec[j];
|
||||
int Lk = lambda[k--];
|
||||
for(int n = 0; n < vecSize; n++)
|
||||
for(UINT16 n = 0; n < vecSize; n++)
|
||||
*Omega++ ^= gmult(*S++, Lk);
|
||||
}
|
||||
}
|
||||
|
|
@ -398,13 +414,13 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
|
|||
for (i = 0; i < erasureCount; i++)
|
||||
{
|
||||
// Only fill _data_ erasures
|
||||
if (erasureLocs[i] >= ndata) break;//return erasureCount;
|
||||
if (erasureLocs[i] >= numData) break;//return erasureCount;
|
||||
|
||||
// evaluate lambda' (derivative) at alpha^(-i)
|
||||
// ( all odd powers disappear)
|
||||
int k = nvecsMinusOne - erasureLocs[i];
|
||||
// (all odd powers disappear)
|
||||
unsigned int k = nvecsMinusOne - erasureLocs[i];
|
||||
int denom = 0;
|
||||
int j;
|
||||
unsigned int j;
|
||||
for (j = 1; j < degree; j += 2)
|
||||
denom ^= gmult(lambda[j], gexp(((255-k)*(j-1)) % 255));
|
||||
// Invert for use computing error value below
|
||||
|
|
@ -431,7 +447,7 @@ int NormDecoder::Decode(char** dVec, int ndata, UINT16 erasureCount, UINT16* era
|
|||
}
|
||||
}
|
||||
return erasureCount;
|
||||
} // end NormDecoder::Decode()
|
||||
} // end NormDecoderRS8a::Decode()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,50 +37,71 @@
|
|||
|
||||
class NormEncoder
|
||||
{
|
||||
// Members
|
||||
private:
|
||||
int npar; // No. of parity packets (n-k)
|
||||
int vector_size; // Size of biggest vector to encode
|
||||
unsigned char* gen_poly; // Ptr to generator polynomial
|
||||
unsigned char* scratch; // scratch space for encoding
|
||||
|
||||
// Methods
|
||||
public:
|
||||
NormEncoder();
|
||||
~NormEncoder();
|
||||
bool Init(int numParity, int vectorSize);
|
||||
void Destroy();
|
||||
bool IsReady(){return (bool)(gen_poly != NULL);}
|
||||
// "Encode" must be called in order of source vector0, vector1, vector2, etc
|
||||
void Encode(const char *dataVector, char **parityVectorList);
|
||||
int NumParity() {return npar;}
|
||||
int VectorSize() {return vector_size;}
|
||||
|
||||
private:
|
||||
bool CreateGeneratorPolynomial();
|
||||
};
|
||||
|
||||
virtual ~NormEncoder();
|
||||
virtual bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize) = 0;
|
||||
virtual void Destroy() = 0;
|
||||
virtual void Encode(const char *dataVector, char **parityVectorList) = 0;
|
||||
}; // end class NormEncoder
|
||||
|
||||
class NormDecoder
|
||||
{
|
||||
public:
|
||||
virtual ~NormDecoder();
|
||||
virtual bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize) = 0;
|
||||
virtual void Destroy() = 0;
|
||||
virtual int Decode(char** vectorList, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs) = 0;
|
||||
}; // end class NormDecoder
|
||||
|
||||
|
||||
|
||||
class NormEncoderRS8a : public NormEncoder
|
||||
{
|
||||
// Methods
|
||||
public:
|
||||
NormEncoderRS8a();
|
||||
~NormEncoderRS8a();
|
||||
bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize);
|
||||
void Destroy();
|
||||
bool IsReady(){return (bool)(gen_poly != NULL);}
|
||||
|
||||
// "Encode" must be called in order of source vector0, vector1, vector2, etc
|
||||
void Encode(const char *dataVector, char **parityVectorList);
|
||||
|
||||
private:
|
||||
bool CreateGeneratorPolynomial();
|
||||
|
||||
// Members
|
||||
unsigned int npar; // No. of parity packets (n-k)
|
||||
UINT16 vector_size; // Size of biggest vector to encode
|
||||
unsigned char* gen_poly; // Ptr to generator polynomial
|
||||
unsigned char* scratch; // scratch space for encoding
|
||||
|
||||
}; // end class NormEncoderRS8a
|
||||
|
||||
|
||||
class NormDecoderRS8a : public NormDecoder
|
||||
{
|
||||
// Methods
|
||||
public:
|
||||
NormDecoderRS8a();
|
||||
~NormDecoderRS8a();
|
||||
bool Init(unsigned int numData, unsigned int numParity, UINT16 vectorSize);
|
||||
int Decode(char** vectorList, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs);
|
||||
int NumParity() {return npar;}
|
||||
int VectorSize() {return vector_size;}
|
||||
void Destroy();
|
||||
|
||||
// Members
|
||||
private:
|
||||
int npar; // No. of parity packets (n-k)
|
||||
int vector_size; // Size of biggest vector to encode
|
||||
unsigned int npar; // No. of parity packets (n-k)
|
||||
UINT16 vector_size; // Size of biggest vector to encode
|
||||
unsigned char* lambda; // Erasure location polynomial ("2*npar" ints)
|
||||
unsigned char** s_vec; // Syndrome vectors (pointers to "npar" vectors)
|
||||
unsigned char** o_vec; // Omega vectors (pointers to "npar" vectors)
|
||||
unsigned char* scratch;
|
||||
// Methods
|
||||
public:
|
||||
NormDecoder();
|
||||
~NormDecoder();
|
||||
bool Init(int numParity, int vectorSize);
|
||||
int Decode(char** vectorList, int ndata, UINT16 erasureCount, UINT16* erasureLocs);
|
||||
int NumParity() {return npar;}
|
||||
int VectorSize() {return vector_size;}
|
||||
void Destroy();
|
||||
};
|
||||
|
||||
}; // end class NormDecoderRS8a
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdio.h> // for rename()
|
||||
#ifdef WIN32
|
||||
#ifndef _WIN32_WCE
|
||||
#include <errno.h>
|
||||
#include <direct.h>
|
||||
#include <share.h>
|
||||
#include <io.h>
|
||||
|
|
@ -47,21 +48,24 @@ bool NormFile::Open(const char* thePath, int theFlags)
|
|||
char tempPath[PATH_MAX];
|
||||
strncpy(tempPath, thePath, PATH_MAX);
|
||||
char* ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
|
||||
if (ptr) *ptr = '\0';
|
||||
ptr = NULL;
|
||||
while (!NormFile::Exists(tempPath))
|
||||
if (NULL != ptr)
|
||||
{
|
||||
char* ptr2 = ptr;
|
||||
ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
|
||||
if (ptr2) *ptr2 = PROTO_PATH_DELIMITER;
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
ptr = NULL;
|
||||
while (!NormFile::Exists(tempPath))
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = tempPath;
|
||||
break;
|
||||
char* ptr2 = ptr;
|
||||
ptr = strrchr(tempPath, PROTO_PATH_DELIMITER);
|
||||
if (ptr2) *ptr2 = PROTO_PATH_DELIMITER;
|
||||
if (ptr)
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = tempPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ptr && ('\0' == *ptr)) *ptr++ = PROTO_PATH_DELIMITER;
|
||||
|
|
@ -162,7 +166,6 @@ bool NormFile::Lock()
|
|||
{
|
||||
#ifndef WIN32 // WIN32 files are automatically locked
|
||||
fchmod(fd, 0640 | S_ISGID);
|
||||
|
||||
#ifdef HAVE_FLOCK
|
||||
if (flock(fd, LOCK_EX | LOCK_NB))
|
||||
return false;
|
||||
|
|
@ -334,50 +337,70 @@ bool NormFile::Rename(const char* oldName, const char* newName)
|
|||
size_t NormFile::Read(char* buffer, size_t len)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
size_t got = 0;
|
||||
while (got < len)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32_WCE
|
||||
size_t result = fread(buffer, 1, len, file_ptr);
|
||||
size_t result = fread(buffer+got, 1, len-got, file_ptr);
|
||||
#else
|
||||
size_t result = _read(fd, buffer, (unsigned int)len);
|
||||
size_t result = _read(fd, buffer+got, (unsigned int)(len-got));
|
||||
#endif // if/else _WIN32_WCE
|
||||
#else
|
||||
ssize_t result = read(fd, buffer, len);
|
||||
ssize_t result = read(fd, buffer+got, len-got);
|
||||
#endif // if/else WIN32
|
||||
if (result < 0)
|
||||
{
|
||||
DMSG(0, "NormFile::Read() read(%d) error: %s (fd = %d, offset:%d)\n", len, GetErrorString(), fd, offset);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (Offset)result;
|
||||
return result;
|
||||
}
|
||||
if (result <= 0)
|
||||
{
|
||||
#ifndef _WIN32_WCE
|
||||
if (EINTR != errno)
|
||||
#endif // !_WIN32_WCE
|
||||
{
|
||||
DMSG(0, "NormFile::Read() read(%d) result:%d error:%s (offset:%d)\n", len, result, GetErrorString(), offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
got += result;
|
||||
offset += (Offset)result;
|
||||
}
|
||||
} // end while (have < want)
|
||||
return got;
|
||||
} // end NormFile::Read()
|
||||
|
||||
size_t NormFile::Write(const char* buffer, size_t len)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
size_t put = 0;
|
||||
while (put < len)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32_WCE
|
||||
size_t result = fwrite(buffer, 1, len, file_ptr);
|
||||
size_t result = fwrite(buffer+put, 1, len-put, file_ptr);
|
||||
#else
|
||||
size_t result = _write(fd, buffer, (unsigned int)len);
|
||||
size_t result = _write(fd, buffer+put, (unsigned int)(len-put));
|
||||
#endif // if/else _WIN32_WCE
|
||||
#else
|
||||
size_t result = write(fd, buffer, len);
|
||||
size_t result = write(fd, buffer+put, len-put);
|
||||
#endif // if/else WIN32
|
||||
if (result < 0)
|
||||
{
|
||||
DMSG(0, "NormFile::Write() write() error: %s\n", GetErrorString());
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (Offset)result;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
if (result <= 0)
|
||||
{
|
||||
#ifndef _WIN32_WCE
|
||||
if (EINTR != errno)
|
||||
#endif // !_WIN32_WCE
|
||||
{
|
||||
DMSG(0, "NormFile::Write() write(%d) result:%d error: %s\n", len, result, GetErrorString());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offset += (Offset)result;
|
||||
put += result;
|
||||
}
|
||||
} // end while (put < len)
|
||||
return put;
|
||||
} // end NormFile::Write()
|
||||
|
||||
bool NormFile::Seek(Offset theOffset)
|
||||
|
|
@ -395,7 +418,7 @@ bool NormFile::Seek(Offset theOffset)
|
|||
#endif // if/else WIN32
|
||||
if (result == (Offset)-1)
|
||||
{
|
||||
DMSG(0, "NormFile::Seek() lseek() error: %s", GetErrorString());
|
||||
DMSG(0, "NormFile::Seek() lseek() error: %s\n", GetErrorString());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -405,6 +428,28 @@ bool NormFile::Seek(Offset theOffset)
|
|||
}
|
||||
} // end NormFile::Seek()
|
||||
|
||||
bool NormFile::Pad(Offset theOffset)
|
||||
{
|
||||
if (theOffset > GetSize())
|
||||
{
|
||||
if (Seek(theOffset - 1))
|
||||
{
|
||||
char byte = 0;
|
||||
if (1 != Write(&byte, 1))
|
||||
{
|
||||
DMSG(0, "NormFile::Pad() write error: %s\n", GetErrorString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormFile::Pad() seek error: %s\n", GetErrorString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormFile::Pad()
|
||||
|
||||
NormFile::Offset NormFile::GetSize() const
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@
|
|||
#else
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#endif // if/else WIN32
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#endif // if/else _WIN32_WCE
|
||||
|
||||
// From PROTOLIB
|
||||
|
|
@ -56,7 +56,7 @@ class NormFile
|
|||
bool Lock();
|
||||
void Unlock();
|
||||
bool Rename(const char* oldName, const char* newName);
|
||||
bool Unlink(const char *path);
|
||||
static bool Unlink(const char *path);
|
||||
void Close();
|
||||
bool IsOpen() const
|
||||
{
|
||||
|
|
@ -71,6 +71,7 @@ class NormFile
|
|||
bool Seek(Offset theOffset);
|
||||
NormFile::Offset GetOffset() const {return (offset);}
|
||||
NormFile::Offset GetSize() const;
|
||||
bool Pad(Offset theOffset); // if file size is less than theOffset, writes a byte to force filesize
|
||||
|
||||
// static helper methods
|
||||
static NormFile::Type GetType(const char *path);
|
||||
|
|
|
|||
|
|
@ -431,3 +431,118 @@ unsigned char NormQuantizeRtt(double rtt)
|
|||
return ((unsigned char)(ceil(255.0 - (13.0*log(NORM_RTT_MAX/rtt)))));
|
||||
} // end NormQuantizeRtt()
|
||||
|
||||
|
||||
// These are lookup tables to support for efficient NORM field
|
||||
// conversion for those fields that are quantized values
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// The following table provides a lookup of NORM
|
||||
// RTT approximation from the 8-bit "rtt" and
|
||||
// "grtt" fields of some NORM messages. The
|
||||
// following routine was used to generate
|
||||
// this table:
|
||||
//
|
||||
// inline double NormUnquantizeRtt(unsigned char qrtt)
|
||||
// {
|
||||
// return ((qrtt < 31) ?
|
||||
// (((double)(qrtt+1))*(double)NORM_RTT_MIN) :
|
||||
// (NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
|
||||
// }
|
||||
//
|
||||
|
||||
|
||||
const double NORM_RTT[256] =
|
||||
{
|
||||
1.000e-06, 2.000e-06, 3.000e-06, 4.000e-06,
|
||||
5.000e-06, 6.000e-06, 7.000e-06, 8.000e-06,
|
||||
9.000e-06, 1.000e-05, 1.100e-05, 1.200e-05,
|
||||
1.300e-05, 1.400e-05, 1.500e-05, 1.600e-05,
|
||||
1.700e-05, 1.800e-05, 1.900e-05, 2.000e-05,
|
||||
2.100e-05, 2.200e-05, 2.300e-05, 2.400e-05,
|
||||
2.500e-05, 2.600e-05, 2.700e-05, 2.800e-05,
|
||||
2.900e-05, 3.000e-05, 3.100e-05, 3.287e-05,
|
||||
3.550e-05, 3.833e-05, 4.140e-05, 4.471e-05,
|
||||
4.828e-05, 5.215e-05, 5.631e-05, 6.082e-05,
|
||||
6.568e-05, 7.093e-05, 7.660e-05, 8.273e-05,
|
||||
8.934e-05, 9.649e-05, 1.042e-04, 1.125e-04,
|
||||
1.215e-04, 1.313e-04, 1.417e-04, 1.531e-04,
|
||||
1.653e-04, 1.785e-04, 1.928e-04, 2.082e-04,
|
||||
2.249e-04, 2.429e-04, 2.623e-04, 2.833e-04,
|
||||
3.059e-04, 3.304e-04, 3.568e-04, 3.853e-04,
|
||||
4.161e-04, 4.494e-04, 4.853e-04, 5.241e-04,
|
||||
5.660e-04, 6.113e-04, 6.602e-04, 7.130e-04,
|
||||
7.700e-04, 8.315e-04, 8.980e-04, 9.698e-04,
|
||||
1.047e-03, 1.131e-03, 1.222e-03, 1.319e-03,
|
||||
1.425e-03, 1.539e-03, 1.662e-03, 1.795e-03,
|
||||
1.938e-03, 2.093e-03, 2.260e-03, 2.441e-03,
|
||||
2.636e-03, 2.847e-03, 3.075e-03, 3.321e-03,
|
||||
3.586e-03, 3.873e-03, 4.182e-03, 4.517e-03,
|
||||
4.878e-03, 5.268e-03, 5.689e-03, 6.144e-03,
|
||||
6.635e-03, 7.166e-03, 7.739e-03, 8.358e-03,
|
||||
9.026e-03, 9.748e-03, 1.053e-02, 1.137e-02,
|
||||
1.228e-02, 1.326e-02, 1.432e-02, 1.547e-02,
|
||||
1.670e-02, 1.804e-02, 1.948e-02, 2.104e-02,
|
||||
2.272e-02, 2.454e-02, 2.650e-02, 2.862e-02,
|
||||
3.090e-02, 3.338e-02, 3.604e-02, 3.893e-02,
|
||||
4.204e-02, 4.540e-02, 4.903e-02, 5.295e-02,
|
||||
5.718e-02, 6.176e-02, 6.669e-02, 7.203e-02,
|
||||
7.779e-02, 8.401e-02, 9.072e-02, 9.798e-02,
|
||||
1.058e-01, 1.143e-01, 1.234e-01, 1.333e-01,
|
||||
1.439e-01, 1.554e-01, 1.679e-01, 1.813e-01,
|
||||
1.958e-01, 2.114e-01, 2.284e-01, 2.466e-01,
|
||||
2.663e-01, 2.876e-01, 3.106e-01, 3.355e-01,
|
||||
3.623e-01, 3.913e-01, 4.225e-01, 4.563e-01,
|
||||
4.928e-01, 5.322e-01, 5.748e-01, 6.207e-01,
|
||||
6.704e-01, 7.240e-01, 7.819e-01, 8.444e-01,
|
||||
9.119e-01, 9.848e-01, 1.064e+00, 1.149e+00,
|
||||
1.240e+00, 1.340e+00, 1.447e+00, 1.562e+00,
|
||||
1.687e+00, 1.822e+00, 1.968e+00, 2.125e+00,
|
||||
2.295e+00, 2.479e+00, 2.677e+00, 2.891e+00,
|
||||
3.122e+00, 3.372e+00, 3.641e+00, 3.933e+00,
|
||||
4.247e+00, 4.587e+00, 4.953e+00, 5.349e+00,
|
||||
5.777e+00, 6.239e+00, 6.738e+00, 7.277e+00,
|
||||
7.859e+00, 8.487e+00, 9.166e+00, 9.898e+00,
|
||||
1.069e+01, 1.154e+01, 1.247e+01, 1.346e+01,
|
||||
1.454e+01, 1.570e+01, 1.696e+01, 1.832e+01,
|
||||
1.978e+01, 2.136e+01, 2.307e+01, 2.491e+01,
|
||||
2.691e+01, 2.906e+01, 3.138e+01, 3.389e+01,
|
||||
3.660e+01, 3.953e+01, 4.269e+01, 4.610e+01,
|
||||
4.979e+01, 5.377e+01, 5.807e+01, 6.271e+01,
|
||||
6.772e+01, 7.314e+01, 7.899e+01, 8.530e+01,
|
||||
9.212e+01, 9.949e+01, 1.074e+02, 1.160e+02,
|
||||
1.253e+02, 1.353e+02, 1.462e+02, 1.578e+02,
|
||||
1.705e+02, 1.841e+02, 1.988e+02, 2.147e+02,
|
||||
2.319e+02, 2.504e+02, 2.704e+02, 2.921e+02,
|
||||
3.154e+02, 3.406e+02, 3.679e+02, 3.973e+02,
|
||||
4.291e+02, 4.634e+02, 5.004e+02, 5.404e+02,
|
||||
5.836e+02, 6.303e+02, 6.807e+02, 7.351e+02,
|
||||
7.939e+02, 8.574e+02, 9.260e+02, 1.000e+03
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// The following tables is provides a lookup
|
||||
// of NORM group size approximation from the
|
||||
// 4-bit "gsize" field of some NORM messages
|
||||
// The following routine was used to generate
|
||||
// this table
|
||||
//
|
||||
// inline double NormUnquantizeGroupSize(unsigned char gsize)
|
||||
// {
|
||||
// double exponent = (double)((gsize & 0x07) + 1);
|
||||
// double mantissa = (0 != (gsize & 0x08)) ? 5.0 : 1.0;
|
||||
// return (mantissa * pow(10.0, exponent));
|
||||
// }
|
||||
//
|
||||
|
||||
const double NORM_GSIZE[16] =
|
||||
{
|
||||
1.000e+01, 1.000e+02, 1.000e+03, 1.000e+04,
|
||||
1.000e+05, 1.000e+06, 1.000e+07, 1.000e+08,
|
||||
5.000e+01, 5.000e+02, 5.000e+03, 5.000e+04,
|
||||
5.000e+05, 5.000e+06, 5.000e+07, 5.000e+08
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,20 +51,20 @@ const double NORM_GRTT_MIN = 0.001; // 1 msec
|
|||
const double NORM_GRTT_MAX = 15.0; // 15 sec
|
||||
const double NORM_RTT_MIN = 1.0e-06;
|
||||
const double NORM_RTT_MAX = 1000.0;
|
||||
extern const double NORM_RTT[];
|
||||
inline double NormUnquantizeRtt(unsigned char qrtt)
|
||||
{
|
||||
return ((qrtt < 31) ?
|
||||
(((double)(qrtt+1))*(double)NORM_RTT_MIN) :
|
||||
(NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
|
||||
return NORM_RTT[qrtt];
|
||||
}
|
||||
unsigned char NormQuantizeRtt(double rtt);
|
||||
|
||||
|
||||
extern const double NORM_GSIZE[];
|
||||
inline double NormUnquantizeGroupSize(unsigned char gsize)
|
||||
{
|
||||
double exponent = (double)((gsize & 0x07) + 1);
|
||||
double mantissa = (0 != (gsize & 0x08)) ? 5.0 : 1.0;
|
||||
return (mantissa * pow(10.0, exponent));
|
||||
return NORM_GSIZE[gsize];
|
||||
}
|
||||
|
||||
inline unsigned char NormQuantizeGroupSize(double gsize)
|
||||
{
|
||||
unsigned char ebits = (unsigned char)log10(gsize);
|
||||
|
|
@ -1386,6 +1386,7 @@ class NormCmdApplicationMsg : public NormCmdMsg
|
|||
class NormNackMsg : public NormMsg
|
||||
{
|
||||
public:
|
||||
enum {DEFAULT_LENGTH_MAX = 40};
|
||||
void Init()
|
||||
{
|
||||
SetType(NACK);
|
||||
|
|
|
|||
|
|
@ -40,14 +40,13 @@ NormCCNode::~NormCCNode()
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
const double NormServerNode::DEFAULT_NOMINAL_INTERVAL = 2*NormSession::DEFAULT_GRTT_ESTIMATE;
|
||||
const double NormServerNode::ACTIVITY_INTERVAL_MIN = 1.0; // 1 second min activity timeout
|
||||
|
||||
NormServerNode::NormServerNode(class NormSession& theSession, NormNodeId nodeId)
|
||||
: NormNode(theSession, nodeId), instance_id(0), synchronized(false), sync_id(0),
|
||||
max_pending_range(256), is_open(false), segment_size(0), ndata(0), nparity(0),
|
||||
repair_boundary(BLOCK_BOUNDARY), erasure_loc(NULL),
|
||||
repair_boundary(BLOCK_BOUNDARY), decoder(NULL), erasure_loc(NULL),
|
||||
retrieval_loc(NULL), retrieval_pool(NULL),
|
||||
cc_sequence(0), cc_enable(false), cc_rate(0.0),
|
||||
rtt_confirmed(false), is_clr(false), is_plr(false),
|
||||
|
|
@ -228,20 +227,28 @@ bool NormServerNode::AllocateBuffers(UINT16 segmentSize,
|
|||
}
|
||||
retrieval_index = 0;
|
||||
|
||||
if (!(retrieval_loc = new UINT16[numData]))
|
||||
if (!(retrieval_loc = new unsigned int[numData]))
|
||||
{
|
||||
DMSG(0, "NormServerNode::AllocateBuffers() retrieval_loc allocation error: %s\n", GetErrorString());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decoder.Init(numParity, segmentSize+NormDataMsg::GetStreamPayloadHeaderLength()))
|
||||
if (NULL != decoder) delete decoder;
|
||||
if (NULL == (decoder = new NormDecoderRS8a))
|
||||
{
|
||||
DMSG(0, "NormServerNode::AllocateBuffers() new NormDecoderRSa error: %s\n", GetErrorString());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decoder->Init(numData, numParity, segmentSize+NormDataMsg::GetStreamPayloadHeaderLength()))
|
||||
{
|
||||
DMSG(0, "NormServerNode::AllocateBuffers() decoder init error\n");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (!(erasure_loc = new UINT16[numParity]))
|
||||
if (!(erasure_loc = new unsigned int[numParity]))
|
||||
{
|
||||
DMSG(0, "NormServerNode::AllocateBuffers() erasure_loc allocation error: %s\n", GetErrorString());
|
||||
Close();
|
||||
|
|
@ -262,7 +269,12 @@ void NormServerNode::FreeBuffers()
|
|||
delete[] erasure_loc;
|
||||
erasure_loc = NULL;
|
||||
}
|
||||
decoder.Destroy();
|
||||
if (NULL != decoder)
|
||||
{
|
||||
decoder->Destroy();
|
||||
delete decoder;
|
||||
decoder = NULL;
|
||||
}
|
||||
if (retrieval_loc)
|
||||
{
|
||||
delete[] retrieval_loc;
|
||||
|
|
@ -270,7 +282,7 @@ void NormServerNode::FreeBuffers()
|
|||
}
|
||||
if (retrieval_pool)
|
||||
{
|
||||
for (UINT16 i = 0; i < ndata; i++)
|
||||
for (unsigned int i = 0; i < ndata; i++)
|
||||
{
|
||||
if (retrieval_pool[i])
|
||||
{
|
||||
|
|
@ -285,10 +297,9 @@ void NormServerNode::FreeBuffers()
|
|||
NormObject* obj;
|
||||
while ((obj = rx_table.Find(rx_table.RangeLo())))
|
||||
{
|
||||
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
|
||||
UINT16 objectId = obj->GetId();
|
||||
DeleteObject(obj);
|
||||
// We do the following to remember which objects were pending
|
||||
AbortObject(obj);
|
||||
// We do the following to remember which _objects_ were pending
|
||||
rx_pending_mask.Set(objectId);
|
||||
}
|
||||
|
||||
|
|
@ -304,6 +315,7 @@ void NormServerNode::UpdateGrttEstimate(UINT8 grttQuantized)
|
|||
DMSG(4, "NormServerNode::UpdateGrttEstimate() node>%lu server>%lu new grtt: %lf sec\n",
|
||||
LocalNodeId(), GetId(), grtt_estimate);
|
||||
// activity timer depends upon sender's grtt estimate
|
||||
// (TBD) do a proper rescaling here instead?
|
||||
double activityInterval = 2*NORM_ROBUST_FACTOR*grtt_estimate;
|
||||
if (activityInterval < ACTIVITY_INTERVAL_MIN) activityInterval = ACTIVITY_INTERVAL_MIN;
|
||||
activity_timer.SetInterval(activityInterval);
|
||||
|
|
@ -361,10 +373,12 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
|
|||
cc.GetSendTime(grtt_send_time);
|
||||
cc_sequence = cc.GetCCSequence();
|
||||
NormCCRateExtension ext;
|
||||
bool hasCCRateExtension = false;
|
||||
while (cc.GetNextExtension(ext))
|
||||
{
|
||||
if (NormHeaderExtension::CC_RATE == ext.GetType())
|
||||
{
|
||||
hasCCRateExtension = true;
|
||||
cc_enable = true;
|
||||
send_rate = NormUnquantizeRate(ext.GetSendRate());
|
||||
// Are we in the cc_node_list?
|
||||
|
|
@ -444,6 +458,8 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
|
|||
session.ActivateTimer(cc_timer);
|
||||
} // end if (CC_RATE == ext.GetType())
|
||||
} // end while (GetNextExtension())
|
||||
// Disable CC feedback if sender doesn't want it
|
||||
if (!hasCCRateExtension && cc_enable) cc_enable = false;
|
||||
break;
|
||||
}
|
||||
case NormCmdMsg::FLUSH:
|
||||
|
|
@ -468,7 +484,9 @@ void NormServerNode::HandleCommand(const struct timeval& currentTime,
|
|||
if (doAck)
|
||||
{
|
||||
// Force sync since we're expected to ACK
|
||||
// and request repair for object indicated
|
||||
Sync(flush.GetObjectId());
|
||||
SetPending(flush.GetObjectId());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1032,47 +1050,66 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
|
|||
if (NULL != obj)
|
||||
{
|
||||
rx_table.Insert(obj);
|
||||
NormFtiExtension fti;
|
||||
while (msg.GetNextExtension(fti))
|
||||
if (129 == msg.GetFecId())
|
||||
{
|
||||
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 (obj->Open(fti.GetObjectSize(),
|
||||
msg.FlagIsSet(NormObjectMsg::FLAG_INFO),
|
||||
fti.GetSegmentSize(),
|
||||
fti.GetFecMaxBlockLen(),
|
||||
fti.GetFecNumParity()))
|
||||
if (NormHeaderExtension::FTI == fti.GetType())
|
||||
{
|
||||
session.Notify(NormController::RX_OBJECT_NEW, this, obj);
|
||||
if (obj->Accepted())
|
||||
#ifndef ASSUME_MDP_FEC
|
||||
if (0 != fti.GetFecInstanceId())
|
||||
{
|
||||
// (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");
|
||||
DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC instance id\n");
|
||||
DeleteObject(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
else
|
||||
#endif // !ASSUME_MDP_FEC
|
||||
if (obj->Open(fti.GetObjectSize(),
|
||||
msg.FlagIsSet(NormObjectMsg::FLAG_INFO),
|
||||
fti.GetSegmentSize(),
|
||||
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
|
||||
{
|
||||
DMSG(0, "NormServerNode::HandleObjectMessage() error opening object\n");
|
||||
DeleteObject(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((NULL != obj) && !obj->IsOpen())
|
||||
{
|
||||
DMSG(0, "NormServerNode::HandleObjectMessage() node>%lu server>%lu "
|
||||
"new obj>%hu - no FTI provided!\n", LocalNodeId(), GetId(), (UINT16)objectId);
|
||||
DeleteObject(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
if (obj && !obj->IsOpen())
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormServerNode::HandleObjectMessage() node>%lu server>%lu "
|
||||
"new obj>%hu - no FTI provided!\n", LocalNodeId(), GetId(), (UINT16)objectId);
|
||||
DMSG(0, "NormServerNode::HandleObjectMessage() error: invalid FEC type\n");
|
||||
DeleteObject(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
|
|
@ -1090,7 +1127,14 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
|
|||
if (obj)
|
||||
{
|
||||
obj->HandleObjectMessage(msg, msgType, blockId, segmentId);
|
||||
if (!obj->IsPending())
|
||||
|
||||
bool objIsPending = obj->IsPending();
|
||||
|
||||
// Silent receivers may be configured to allow obj completion w/out INFO
|
||||
if (objIsPending && session.RcvrIgnoreInfo())
|
||||
objIsPending = obj->PendingMaskIsSet();
|
||||
|
||||
if (!objIsPending)
|
||||
{
|
||||
// Reliable reception of this object has completed
|
||||
if (NormObject::FILE == obj->GetType())
|
||||
|
|
@ -1102,7 +1146,7 @@ void NormServerNode::HandleObjectMessage(const NormObjectMsg& msg)
|
|||
if (NormObject::STREAM != obj->GetType())
|
||||
{
|
||||
// Streams never complete unless they are "closed" by sender
|
||||
// and this handled within stream control code in "normObject.cpp"
|
||||
// and this is handled within stream control code in "normObject.cpp"
|
||||
session.Notify(NormController::RX_OBJECT_COMPLETED, this, obj);
|
||||
DeleteObject(obj);
|
||||
obj = NULL;
|
||||
|
|
@ -1145,6 +1189,21 @@ bool NormServerNode::SyncTest(const NormObjectMsg& msg) const
|
|||
|
||||
} // end NormServerNode::SyncTest()
|
||||
|
||||
// a little helper method
|
||||
void NormServerNode::AbortObject(NormObject* obj)
|
||||
{
|
||||
// it it's a file, close it first, so app can do something
|
||||
if (NormObject::FILE == obj->GetType())
|
||||
#ifdef SIMULATE
|
||||
static_cast<NormSimObject*>(obj)->Close();
|
||||
#else
|
||||
static_cast<NormFileObject*>(obj)->Close();
|
||||
#endif // !SIMULATE
|
||||
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
|
||||
DeleteObject(obj);
|
||||
failure_count++;
|
||||
} // end NormServerNode::AbortObject()
|
||||
|
||||
|
||||
// This method establishes the sync point "sync_id"
|
||||
// objectId. The sync point is the first ordinal
|
||||
|
|
@ -1176,9 +1235,7 @@ void NormServerNode::Sync(NormObjectId objectId)
|
|||
NormObject* obj;
|
||||
while ((obj = rx_table.Find(rx_table.RangeLo())))
|
||||
{
|
||||
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
|
||||
DeleteObject(obj);
|
||||
failure_count++;
|
||||
AbortObject(obj);
|
||||
}
|
||||
rx_pending_mask.Clear();
|
||||
}
|
||||
|
|
@ -1188,9 +1245,7 @@ void NormServerNode::Sync(NormObjectId objectId)
|
|||
while ((obj = rx_table.Find(rx_table.RangeLo())) &&
|
||||
(obj->GetId() < objectId))
|
||||
{
|
||||
session.Notify(NormController::RX_OBJECT_ABORTED, this, obj);
|
||||
DeleteObject(obj);
|
||||
failure_count++;
|
||||
AbortObject(obj);
|
||||
}
|
||||
unsigned long numBits = (UINT16)(objectId - firstPending) + 1;
|
||||
rx_pending_mask.UnsetBits(firstPending, numBits);
|
||||
|
|
@ -1495,7 +1550,10 @@ bool NormServerNode::OnRepairTimeout(ProtoTimer& /*theTimer*/)
|
|||
{
|
||||
//if (slow_start) // (TBD) should we only set flag on actual slow_start?
|
||||
ext.SetCCFlag(NormCC::START);
|
||||
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
|
||||
{
|
||||
|
|
@ -1568,7 +1626,10 @@ bool NormServerNode::OnRepairTimeout(ProtoTimer& /*theTimer*/)
|
|||
}
|
||||
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.ResetFlags();
|
||||
// Set flags for missing objects according to
|
||||
|
|
@ -2413,7 +2474,7 @@ NormLossEstimator2::NormLossEstimator2()
|
|||
event_window(0), event_index(0),
|
||||
event_window_time(0.0), event_index_time(0.0),
|
||||
seeking_loss_event(true),
|
||||
no_loss(true), initial_loss(0.0), loss_interval(0.0),
|
||||
no_loss(true), initial_loss(0.0),
|
||||
current_discount(1.0)
|
||||
{
|
||||
memset(history, 0, 9*sizeof(unsigned long));
|
||||
|
|
@ -2527,11 +2588,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
|
|||
|
||||
if (seeking_loss_event)
|
||||
{
|
||||
double scale;
|
||||
if (history[0] > loss_interval)
|
||||
scale = 0.125 / (1.0 + log((double)(event_window ? event_window : 1)));
|
||||
else
|
||||
scale = 0.125;
|
||||
if (outageDepth) // non-zero outageDepth means pkt loss(es)
|
||||
{
|
||||
if (no_loss) // first loss
|
||||
|
|
@ -2547,11 +2603,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
|
|||
no_loss = false;
|
||||
}
|
||||
|
||||
// Old method
|
||||
if (loss_interval > 0.0)
|
||||
loss_interval += scale*(((double)history[0]) - loss_interval);
|
||||
else
|
||||
loss_interval = (double) history[0];
|
||||
|
||||
// New method
|
||||
// New loss event, shift loss interval history & discounts
|
||||
|
|
@ -2571,19 +2622,6 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
|
|||
(((double)currentTime.tv_usec)/1.0e06));
|
||||
event_index_time += event_window_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (no_loss) fprintf(stderr, "No loss (seq:%u) ...\n", theSequence);
|
||||
if (loss_interval > 0.0)
|
||||
{
|
||||
double diff = ((double)history[0]) - loss_interval;
|
||||
if (diff >= 1.0)
|
||||
{
|
||||
//scale *= (diff * diff) / (loss_interval * loss_interval);
|
||||
loss_interval += scale*log(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2595,20 +2633,9 @@ bool NormLossEstimator2::Update(const struct timeval& currentTime,
|
|||
return newLossEvent;
|
||||
} // end NormLossEstimator2::Update()
|
||||
|
||||
double NormLossEstimator2::LossFraction()
|
||||
{
|
||||
#if defined0
|
||||
if (use_ewma_loss_estimate)
|
||||
return MdpLossFraction(); // MDP EWMA approach
|
||||
else
|
||||
#endif // SIMULATOR
|
||||
return (TfrcLossFraction()); // ACIRI TFRC approach
|
||||
} // end NormLossEstimator2::LossFraction()
|
||||
|
||||
|
||||
|
||||
// TFRC Loss interval averaging with discounted, weighted averaging
|
||||
double NormLossEstimator2::TfrcLossFraction()
|
||||
double NormLossEstimator2::LossFraction()
|
||||
{
|
||||
if (!history[1]) return 0.0;
|
||||
// Compute older weighted average s1->s8 for discount determination
|
||||
|
|
|
|||
|
|
@ -91,9 +91,7 @@ class NormLossEstimator2
|
|||
unsigned short seqNumber,
|
||||
bool ecn = false);
|
||||
double LossFraction();
|
||||
double MdpLossFraction()
|
||||
{return ((loss_interval > 0.0) ? (1.0/loss_interval) : 0.0);}
|
||||
double TfrcLossFraction();
|
||||
|
||||
bool NoLoss() {return no_loss;}
|
||||
void SetInitialLoss(double lossFraction)
|
||||
{
|
||||
|
|
@ -121,8 +119,6 @@ class NormLossEstimator2
|
|||
bool no_loss;
|
||||
double initial_loss;
|
||||
|
||||
double loss_interval; // EWMA of loss event interval
|
||||
|
||||
unsigned long history[9]; // loss interval history
|
||||
double discount[9];
|
||||
double current_discount;
|
||||
|
|
@ -296,6 +292,8 @@ class NormServerNode : public NormNode
|
|||
}
|
||||
void SetPending(NormObjectId objectId);
|
||||
|
||||
void AbortObject(NormObject* obj);
|
||||
|
||||
void DeleteObject(NormObject* obj);
|
||||
|
||||
UINT16 SegmentSize() {return segment_size;}
|
||||
|
|
@ -340,7 +338,7 @@ class NormServerNode : public NormNode
|
|||
|
||||
UINT16 Decode(char** segmentList, UINT16 numData, UINT16 erasureCount)
|
||||
{
|
||||
return decoder.Decode(segmentList, numData, erasureCount, erasure_loc);
|
||||
return decoder->Decode(segmentList, numData, erasureCount, erasure_loc);
|
||||
}
|
||||
|
||||
void CalculateGrttResponse(const struct timeval& currentTime,
|
||||
|
|
@ -397,8 +395,8 @@ class NormServerNode : public NormNode
|
|||
|
||||
bool is_open;
|
||||
UINT16 segment_size;
|
||||
UINT16 ndata;
|
||||
UINT16 nparity;
|
||||
unsigned int ndata;
|
||||
unsigned int nparity;
|
||||
|
||||
NormObjectTable rx_table;
|
||||
ProtoSlidingMask rx_pending_mask;
|
||||
|
|
@ -408,11 +406,11 @@ class NormServerNode : public NormNode
|
|||
bool unicast_nacks;
|
||||
NormBlockPool block_pool;
|
||||
NormSegmentPool segment_pool;
|
||||
NormDecoder decoder;
|
||||
UINT16* erasure_loc;
|
||||
UINT16* retrieval_loc;
|
||||
NormDecoder* decoder;
|
||||
unsigned int* erasure_loc;
|
||||
unsigned int* retrieval_loc;
|
||||
char** retrieval_pool;
|
||||
UINT16 retrieval_index;
|
||||
unsigned int retrieval_index;
|
||||
|
||||
bool server_active;
|
||||
ProtoTimer activity_timer;
|
||||
|
|
|
|||
|
|
@ -131,16 +131,16 @@ bool NormObject::Open(const NormObjectSize& objectSize,
|
|||
}
|
||||
|
||||
// Init pending_mask (everything pending)
|
||||
if (!pending_mask.Init(numBlocks.LSB(), 0xffffffff))
|
||||
if (!pending_mask.Init(numBlocks.LSB(), (UINT32)0xffffffff))
|
||||
{
|
||||
DMSG(0, "NormObject::Open() init pending_mask (%lu) error: %s\n",
|
||||
numBlocks.GetOffset(), GetErrorString());
|
||||
numBlocks.LSB(), GetErrorString());
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init repair_mask
|
||||
if (!repair_mask.Init(numBlocks.LSB(), 0xffffffff))
|
||||
if (!repair_mask.Init(numBlocks.LSB(), (UINT32)0xffffffff))
|
||||
{
|
||||
DMSG(0, "NormObject::Open() init pending_mask error\n");
|
||||
Close();
|
||||
|
|
@ -367,7 +367,7 @@ bool NormObject::ActivateRepairs()
|
|||
GetLastRepair(lastId); // for debug output only
|
||||
DMSG(6, "NormObject::ActivateRepairs() node>%lu obj>%hu activating blk>%lu->%lu block repairs ...\n",
|
||||
LocalNodeId(), (UINT16)transport_id, (UINT32)nextId, (UINT32)lastId);
|
||||
repairsActivated = true;
|
||||
//repairsActivated = true;
|
||||
UINT16 autoParity = session.ServerAutoParity();
|
||||
do
|
||||
{
|
||||
|
|
@ -375,7 +375,9 @@ bool NormObject::ActivateRepairs()
|
|||
if (block) block->TxReset(GetBlockSize(nextId), nparity, autoParity, segment_size);
|
||||
// (TBD) This check can be eventually eliminated if everything else is done right
|
||||
if (!pending_mask.Set(nextId))
|
||||
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error!\n", (UINT32)nextId);
|
||||
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error 1!\n", (UINT32)nextId);
|
||||
else
|
||||
repairsActivated = true;
|
||||
nextId++;
|
||||
} while (GetNextRepair(nextId));
|
||||
repair_mask.Clear();
|
||||
|
|
@ -388,12 +390,14 @@ bool NormObject::ActivateRepairs()
|
|||
{
|
||||
if (block->ActivateRepairs(nparity))
|
||||
{
|
||||
repairsActivated = true;
|
||||
//repairsActivated = true;
|
||||
DMSG(6, "NormObject::ActivateRepairs() node>%lu obj>%hu activated blk>%lu segment repairs ...\n",
|
||||
LocalNodeId(), (UINT16)transport_id, (UINT32)block->GetId());
|
||||
// (TBD) This check can be eventually eliminated if everything else is done right
|
||||
if (!pending_mask.Set(block->GetId()))
|
||||
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error!\n", (UINT32)block->GetId());
|
||||
DMSG(0, "NormObject::ActivateRepairs() pending_mask.Set(%lu) error 2!\n", (UINT32)block->GetId());
|
||||
else
|
||||
repairsActivated = true;
|
||||
}
|
||||
}
|
||||
return repairsActivated;
|
||||
|
|
@ -441,8 +445,6 @@ bool NormObject::AppendRepairAdv(NormCmdRepairAdvMsg& cmd)
|
|||
{
|
||||
NormBlockId currentId = nextId;
|
||||
nextId++;
|
||||
//DMSG(0, "NormObject::AppendRepairAdv() testing block:%lu nextId:%lu endId:%lu\n",
|
||||
// (UINT32)currentId, (UINT32)nextId, (UINT32)endId);
|
||||
bool repairEntireBlock = repair_mask.Test(currentId);
|
||||
if (repairEntireBlock)
|
||||
{
|
||||
|
|
@ -673,7 +675,7 @@ bool NormObject::PassiveRepairCheck(NormBlockId blockId,
|
|||
NormSegmentId firstPendingSegment;
|
||||
if (block->GetFirstPending(firstPendingSegment))
|
||||
{
|
||||
if (segmentId > firstPendingSegment)
|
||||
if (firstPendingSegment <= segmentId)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
|
@ -700,11 +702,13 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
{
|
||||
// (TBD) make sure it's OK for "max_pending_segment"
|
||||
// and "next_segment_id" to be > blockSize
|
||||
bool thruObject = false;
|
||||
switch (level)
|
||||
{
|
||||
case TO_OBJECT:
|
||||
return false;
|
||||
case THRU_INFO:
|
||||
// (TBD) set "thruObject" if info-only obj
|
||||
break;
|
||||
case TO_BLOCK:
|
||||
if (blockId >= max_pending_block)
|
||||
|
|
@ -712,6 +716,7 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
max_pending_block = blockId;
|
||||
max_pending_segment = 0;
|
||||
}
|
||||
// (TBD) set "thruObject" if info-only obj
|
||||
break;
|
||||
case THRU_SEGMENT:
|
||||
if (blockId > max_pending_block)
|
||||
|
|
@ -724,6 +729,12 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
if (segmentId >= max_pending_segment)
|
||||
max_pending_segment = segmentId + 1;
|
||||
}
|
||||
if (blockId == final_block_id)
|
||||
{
|
||||
unsigned int finalSegment = GetBlockSize(blockId) - 1;
|
||||
if (finalSegment <= segmentId)
|
||||
thruObject = true;
|
||||
}
|
||||
break;
|
||||
case THRU_BLOCK:
|
||||
if (blockId > max_pending_block)
|
||||
|
|
@ -731,6 +742,8 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
max_pending_block = blockId;
|
||||
max_pending_segment = GetBlockSize(blockId);
|
||||
}
|
||||
if (blockId == final_block_id)
|
||||
thruObject = true;
|
||||
break;
|
||||
case THRU_OBJECT:
|
||||
if (!IsStream())
|
||||
|
|
@ -738,11 +751,19 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
else if (blockId > max_pending_block)
|
||||
max_pending_block = blockId;
|
||||
max_pending_segment = GetBlockSize(max_pending_block);
|
||||
thruObject = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // end switch (level)
|
||||
|
||||
if (thruObject && session.RcvrIsLowDelay())
|
||||
{
|
||||
ASSERT(NULL != server);
|
||||
server->AbortObject(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timerActive)
|
||||
{
|
||||
if (holdoffPhase) return false;
|
||||
|
|
@ -801,9 +822,9 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
if (level > TO_BLOCK) lastId++;
|
||||
while (nextId < lastId)
|
||||
{
|
||||
if (nextId > lastId) break;
|
||||
if (nextId > lastId) break; // (TBD) is this line unecessary?
|
||||
NormBlock* block = block_buffer.Find(nextId);
|
||||
if (block)
|
||||
if (NULL != block)
|
||||
{
|
||||
if ((nextId == blockId) && (THRU_SEGMENT == level))
|
||||
{
|
||||
|
|
@ -854,6 +875,7 @@ bool NormObject::ClientRepairCheck(CheckLevel level,
|
|||
else
|
||||
current_block_id = final_block_id;
|
||||
next_segment_id = GetBlockSize(current_block_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1047,7 +1069,6 @@ bool NormObject::AppendRepairRequest(NormNackMsg& nack,
|
|||
} // end if (appendRequest)
|
||||
nextId++;
|
||||
iterating = GetNextPending(nextId);
|
||||
//DMSG(0, "got next pending>%lu result:%d\n", (UINT32)nextId, iterating);
|
||||
iterating = iterating && (flush || (nextId <= max_pending_block));
|
||||
} // end while (iterating || (0 != consecutiveCount))
|
||||
|
||||
|
|
@ -1119,17 +1140,14 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
UINT16 numData = GetBlockSize(blockId);
|
||||
|
||||
// For stream objects, a little extra mgmt is required
|
||||
NormStreamObject* stream = NULL;
|
||||
if (STREAM == type)
|
||||
{
|
||||
NormStreamObject* stream = static_cast<NormStreamObject*>(this);
|
||||
stream = static_cast<NormStreamObject*>(this);
|
||||
if (!stream->StreamUpdateStatus(blockId))
|
||||
{
|
||||
DMSG(4, "NormObject::HandleObjectMessage() node:%lu server:%lu obj>%hu blk>%lu "
|
||||
"broken stream ...\n", LocalNodeId(), server->GetId(), (UINT16)transport_id, (UINT32)blockId);
|
||||
|
||||
//ASSERT(0);
|
||||
// ??? Ignore this new packet and try to fix stream ???
|
||||
//return;
|
||||
server->IncrementResyncCount();
|
||||
while (!stream->StreamUpdateStatus(blockId))
|
||||
{
|
||||
|
|
@ -1214,22 +1232,6 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
if (isSourceSymbol)
|
||||
{
|
||||
block->DecrementErasureCount();
|
||||
/* STREAM payload flags have been deprecated in favor of "payload_msg_start"
|
||||
// This bit of code makes sure that NORM Protocol Version 1
|
||||
// use of FLAG_MSG_START is observed even when the equivalent (and better!)
|
||||
// stream payload flag is not used.
|
||||
if (IsStream())
|
||||
{
|
||||
if (data.FlagIsSet(NormObjectMsg::FLAG_MSG_START) &&
|
||||
!NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(),
|
||||
NormDataMsg::FLAG_MSG_START))
|
||||
{
|
||||
DMSG(0, "NormObject::HandleObjectMessage() setting missing FLAG_MSG_START\n");
|
||||
NormDataMsg::SetStreamPayloadFlag((char*)data.GetPayload(), NormDataMsg::FLAG_MSG_START);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (WriteSegment(blockId, segmentId, data.GetPayload()))
|
||||
{
|
||||
objectUpdated = true;
|
||||
|
|
@ -1238,7 +1240,10 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
|
|
@ -1297,11 +1302,11 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
}
|
||||
}
|
||||
nextErasure = numData;
|
||||
// Set erasure locs for any missing parity symbol segments
|
||||
// Set erasure locs for any missing parity symbol segments, too!
|
||||
while (block->GetNextPending(nextErasure))
|
||||
server->SetErasureLoc(erasureCount++, nextErasure++);
|
||||
} // end if (nextErasure < numData)
|
||||
} // end (block->GetFirstPending(nextErasure))
|
||||
} // end if (block->GetFirstPending(nextErasure))
|
||||
|
||||
if (erasureCount)
|
||||
{
|
||||
|
|
@ -1315,8 +1320,16 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
{
|
||||
objectUpdated = true;
|
||||
// For statistics only (TBD) #ifdef NORM_DEBUG
|
||||
// "segmentLength" is not necessarily correct here (TBD - fix this)
|
||||
server->IncrementRecvGoodput(segmentLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsStream())
|
||||
DMSG(4, "NormObject::HandleObjectMessage() WriteSegment() error\n");
|
||||
else
|
||||
DMSG(0, "NormObject::HandleObjectMessage() WriteSegment() error\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1337,8 +1350,11 @@ void NormObject::HandleObjectMessage(const NormObjectMsg& msg,
|
|||
// so it's not called unnecessarily
|
||||
if (objectUpdated && notify_on_update)
|
||||
{
|
||||
notify_on_update = false;
|
||||
session.Notify(NormController::RX_OBJECT_UPDATED, server, this);
|
||||
if ((NULL == stream) || stream->IsReadReady())
|
||||
{
|
||||
notify_on_update = false;
|
||||
session.Notify(NormController::RX_OBJECT_UPDATED, server, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1494,6 +1510,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
|
||||
fti.SetSegmentSize(segment_size);
|
||||
fti.SetObjectSize(object_size);
|
||||
fti.SetFecInstanceId(0); // ZERO is for legacy MDP/NORM FEC encoder (TBD - use appropriate instanceId)
|
||||
fti.SetFecMaxBlockLen(ndata);
|
||||
fti.SetFecNumParity(nparity);
|
||||
|
||||
|
|
@ -1510,10 +1527,23 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
{
|
||||
// Attempt to advance stream (probably was repair-delayed)
|
||||
if (IsStream())
|
||||
{
|
||||
static_cast<NormStreamObject*>(this)->StreamAdvance();
|
||||
if (pending_mask.IsSet())
|
||||
{
|
||||
return NextServerMsg(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(IsRepairPending());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
|
@ -1521,7 +1551,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
NormBlock* block = block_buffer.Find(blockId);
|
||||
if (!block)
|
||||
{
|
||||
if (!(block = session.ServerGetFreeBlock(transport_id, blockId)))
|
||||
if (NULL == (block = session.ServerGetFreeBlock(transport_id, blockId)))
|
||||
{
|
||||
DMSG(2, "NormObject::NextServerMsg() node>%lu warning: server resource "
|
||||
"constrained (no free blocks).\n", LocalNodeId());
|
||||
|
|
@ -1558,35 +1588,33 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
{
|
||||
NormBlock* b = block_buffer.Find(block_buffer.RangeLo());
|
||||
ASSERT(NULL != b);
|
||||
//ASSERT(!b->IsPending()); (TBD) ??? Why might "b" be pending?
|
||||
bool push = static_cast<NormStreamObject*>(this)->GetPushMode();
|
||||
if (!push && (b->IsRepairPending() || IsRepairSet(b->GetId())))
|
||||
{
|
||||
// Pending repairs delaying stream advance
|
||||
DMSG(0, "NormObject::NextServerMsg() node>%lu pending repairs delaying stream progress\n", LocalNodeId());
|
||||
DMSG(4, "NormObject::NextServerMsg() node>%lu pending repairs delaying stream progress\n", LocalNodeId());
|
||||
session.ServerPutFreeBlock(block);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prune old non-pending block (or even pending if "push" enabled stream)
|
||||
block_buffer.Remove(b);
|
||||
session.ServerPutFreeBlock(b);
|
||||
continue;
|
||||
// Prune old non-pending block (or even pending if "push" enabled stream)
|
||||
block_buffer.Remove(b);
|
||||
if (IsStream())
|
||||
static_cast<NormStreamObject*>(this)->UnlockBlock(b->GetId());
|
||||
session.ServerPutFreeBlock(b);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (IsStream())
|
||||
{
|
||||
DMSG(0, "NormObject::NextServerMsg() node>%lu Warning! can't repair old block\n", LocalNodeId());
|
||||
DMSG(4, "NormObject::NextServerMsg() node>%lu Warning! can't repair old block\n", LocalNodeId());
|
||||
session.ServerPutFreeBlock(block);
|
||||
pending_mask.Unset(blockId);
|
||||
if (pending_mask.IsSet())
|
||||
{
|
||||
return NextServerMsg(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<NormStreamObject*>(this)->StreamAdvance();
|
||||
return false;
|
||||
}
|
||||
// Unlock (set to non-pending status) the corresponding stream_buffer block
|
||||
static_cast<NormStreamObject*>(this)->UnlockBlock(blockId);
|
||||
return NextServerMsg(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1595,7 +1623,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end if (!block)
|
||||
NormSegmentId segmentId = 0;
|
||||
if (!block->GetFirstPending(segmentId))
|
||||
{
|
||||
|
|
@ -1620,7 +1648,7 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
data->SetPayloadLength(payloadLength);
|
||||
|
||||
// Perform incremental FEC encoding as needed
|
||||
if ((block->ParityReadiness() == segmentId) && nparity)
|
||||
if ((block->ParityReadiness() == segmentId) && (0 != nparity))
|
||||
// (TBD) && ((incrementalParity == true) || (auto_parity != 0))
|
||||
{
|
||||
// (TBD) for non-stream objects, catch alternate "last block/segment len"
|
||||
|
|
@ -1659,8 +1687,12 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
data->SetFecSymbolId(segmentId);
|
||||
if (!block->IsPending())
|
||||
{
|
||||
// End of block reached
|
||||
block->ResetParityCount(nparity);
|
||||
pending_mask.Unset(blockId);
|
||||
// for EMCON sending, mark NORM_INFO for re-transmission, if applicable
|
||||
if (session.SndrEmcon() && HaveInfo())
|
||||
pending_info = true;
|
||||
}
|
||||
|
||||
if (!pending_mask.IsSet())
|
||||
|
|
@ -1682,8 +1714,10 @@ bool NormObject::NextServerMsg(NormObjectMsg* msg)
|
|||
return true;
|
||||
} // end NormObject::NextServerMsg()
|
||||
|
||||
|
||||
void NormStreamObject::StreamAdvance()
|
||||
{
|
||||
// (TBD) should we make sure !pending_mask.IsSet()???
|
||||
NormBlockId nextBlockId = stream_next_id;
|
||||
// Make sure we won't prevent any pending repairs
|
||||
if (repair_mask.CanSet(nextBlockId))
|
||||
|
|
@ -1693,7 +1727,8 @@ void NormStreamObject::StreamAdvance()
|
|||
if (pending_mask.Set(nextBlockId))
|
||||
stream_next_id++;
|
||||
else
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() error setting stream pending mask (1)\n");
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() error: node>%lu couldn't set set stream pending mask (1)\n",
|
||||
LocalNodeId());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1706,18 +1741,20 @@ void NormStreamObject::StreamAdvance()
|
|||
if (pending_mask.Set(nextBlockId))
|
||||
stream_next_id++;
|
||||
else
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() error setting stream pending mask (2)\n");
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() error: node>%lu couldn't set stream pending mask (2)\n",
|
||||
LocalNodeId());
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() warning: node>%lu Pending segment repairs (blk>%lu) "
|
||||
DMSG(4, "NormStreamObject::StreamAdvance() warning: node>%lu pending segment repairs (blk>%lu) "
|
||||
"delaying stream advance ...\n", LocalNodeId(), (UINT32)block->GetId());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormStreamObject::StreamAdvance() pending block repair delaying stream advance ...\n");
|
||||
DMSG(4, "NormStreamObject::StreamAdvance() warning: node>%lu pending block repair delaying stream advance ...\n",
|
||||
LocalNodeId());
|
||||
}
|
||||
} // end NormStreamObject::StreamAdvance()
|
||||
|
||||
|
|
@ -1914,6 +1951,8 @@ bool NormFileObject::Accept(const char* thePath)
|
|||
void NormFileObject::Close()
|
||||
{
|
||||
NormObject::Close();
|
||||
if (NULL != server) // we've been receiving this file
|
||||
file.Unlock();
|
||||
file.Close();
|
||||
} // end NormFileObject::Close()
|
||||
|
||||
|
|
@ -2277,9 +2316,8 @@ NormStreamObject::NormStreamObject(class NormSession& theSession,
|
|||
class NormServerNode* theServer,
|
||||
const NormObjectId& objectId)
|
||||
: NormObject(STREAM, theSession, theServer, objectId),
|
||||
stream_sync(false), sync_offset_valid(false),
|
||||
write_vacancy(false), posted_tx_queue_vacancy(false),
|
||||
read_init(true),
|
||||
stream_sync(false), write_vacancy(false),
|
||||
read_init(true), read_ready(false),
|
||||
flush_pending(false), msg_start(true),
|
||||
flush_mode(FLUSH_NONE), push_mode(false),
|
||||
stream_closing(false),
|
||||
|
|
@ -2362,7 +2400,6 @@ bool NormStreamObject::Open(UINT32 bufferSize,
|
|||
tx_index.block = tx_index.segment = 0;
|
||||
tx_offset = write_offset = read_offset = 0;
|
||||
write_vacancy = true;
|
||||
posted_tx_queue_vacancy = false;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
|
|
@ -2381,7 +2418,6 @@ bool NormStreamObject::Open(UINT32 bufferSize,
|
|||
}
|
||||
|
||||
stream_sync = false;
|
||||
sync_offset_valid = false;
|
||||
flush_pending = false;
|
||||
msg_start = true;
|
||||
stream_closing = false;
|
||||
|
|
@ -2445,6 +2481,12 @@ bool NormStreamObject::LockBlocks(NormBlockId firstId, NormBlockId lastId)
|
|||
return true;
|
||||
} // end NormStreamObject::LockBlocks()
|
||||
|
||||
void NormStreamObject::UnlockBlock(NormBlockId blockId)
|
||||
{
|
||||
NormBlock* block = stream_buffer.Find(blockId);
|
||||
if (NULL != block) block->ClearPending();
|
||||
} // end NormStreamObject::UnlockBlock()
|
||||
|
||||
|
||||
bool NormStreamObject::LockSegments(NormBlockId blockId, NormSegmentId firstId, NormSegmentId lastId)
|
||||
{
|
||||
|
|
@ -2495,7 +2537,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
|
|||
else
|
||||
{
|
||||
// Stream broken
|
||||
//DMSG(0, "NormObject::StreamUpdateStatus() broken 1 ...\n");
|
||||
DMSG(4, "NormObject::StreamUpdateStatus() broken 1 ...\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -2505,7 +2547,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
|
|||
if (delta > NormBlockId(pending_mask.GetSize()))
|
||||
{
|
||||
// Stream broken
|
||||
//DMSG(0, "NormObject::StreamUpdateStatus() broken 2 ...\n");
|
||||
DMSG(4, "NormObject::StreamUpdateStatus() broken 2 ...\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
|
@ -2538,7 +2580,7 @@ bool NormStreamObject::StreamUpdateStatus(NormBlockId blockId)
|
|||
stream_sync_id = blockId;
|
||||
stream_next_id = blockId + pending_mask.GetSize();
|
||||
// Since we're doing a resync including "read_init", dump any buffered data
|
||||
// (TBD) this may not be necessary and is thus currently commented-out code
|
||||
// (TBD) this may not be necessary??? and is thus currently commented-out code
|
||||
/*read_init = true;
|
||||
NormBlock* block;
|
||||
while (NULL != (block = stream_buffer.Find(stream_buffer.RangeLo())))
|
||||
|
|
@ -2578,6 +2620,7 @@ UINT16 NormStreamObject::ReadSegment(NormBlockId blockId,
|
|||
//DMSG(0, "NormStreamObject::ReadSegment() stream starved (1)\n");
|
||||
return false;
|
||||
}
|
||||
// (TBD) should we check to see if "blockId > write_index.block" ?
|
||||
if ((blockId == write_index.block) &&
|
||||
(segmentId >= write_index.segment))
|
||||
{
|
||||
|
|
@ -2624,11 +2667,6 @@ UINT16 NormStreamObject::ReadSegment(NormBlockId blockId,
|
|||
}
|
||||
}
|
||||
}
|
||||
/*if (HasVacancy() && !posted_tx_queue_vacancy)
|
||||
{
|
||||
posted_tx_queue_vacancy = true;
|
||||
session.Notify(NormController::TX_QUEUE_VACANCY, NULL, this);
|
||||
} */
|
||||
|
||||
UINT16 segmentLength = NormDataMsg::ReadStreamPayloadLength(segment);
|
||||
ASSERT(segmentLength <= segment_size);
|
||||
|
|
@ -2649,13 +2687,13 @@ bool NormStreamObject::WriteSegment(NormBlockId blockId,
|
|||
const char* segment)
|
||||
{
|
||||
UINT32 segmentOffset = NormDataMsg::ReadStreamPayloadOffset(segment);
|
||||
|
||||
if (read_init)
|
||||
{
|
||||
read_init = false;
|
||||
read_index.block = blockId;
|
||||
read_index.segment = segmentId;
|
||||
read_offset = segmentOffset;
|
||||
read_ready = true;
|
||||
}
|
||||
|
||||
if ((blockId < read_index.block) ||
|
||||
|
|
@ -2715,7 +2753,7 @@ bool NormStreamObject::WriteSegment(NormBlockId blockId,
|
|||
{
|
||||
// App didn't want any data here, purge segment
|
||||
//ASSERT(0);
|
||||
dataLost = true;
|
||||
dataLost = true;
|
||||
block->UnsetPending(read_index.segment++);
|
||||
if (read_index.segment >= ndata)
|
||||
{
|
||||
|
|
@ -2779,13 +2817,29 @@ bool NormStreamObject::WriteSegment(NormBlockId blockId,
|
|||
memcpy(s, segment, payloadLength);
|
||||
block->AttachSegment(segmentId, s);
|
||||
block->SetPending(segmentId);
|
||||
ASSERT(block->GetSegment(segmentId) == s);
|
||||
}
|
||||
if (!sync_offset_valid)
|
||||
{
|
||||
// Set "sync_offset" on first received data buffered.
|
||||
sync_offset = segmentOffset;
|
||||
sync_offset_valid = true;
|
||||
|
||||
if (!read_ready)
|
||||
{
|
||||
// Did this segment make our stream ready for reading
|
||||
|
||||
if ((blockId == read_index.block) && (segmentId == read_index.segment))
|
||||
{
|
||||
read_ready = true;
|
||||
}
|
||||
else if (block_pool.GetCount() < block_pool_threshold)
|
||||
{
|
||||
// We're starting to fill up receive stream buffer
|
||||
// with alot of partially received coding blocks
|
||||
read_ready = true;
|
||||
}
|
||||
else if (session.RcvrIsLowDelay())
|
||||
{
|
||||
INT32 delta = blockId - read_index.block;
|
||||
if (delta > session.RcvrGetMaxDelay())
|
||||
read_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
} // end NormStreamObject::WriteSegment()
|
||||
|
|
@ -2845,8 +2899,18 @@ void NormStreamObject::Prune(NormBlockId blockId, bool updateStatus)
|
|||
}
|
||||
} // end NormStreamObject::Prune()
|
||||
|
||||
// Sequential (in order) read/write routines (TBD) Add a "Seek()" method
|
||||
|
||||
bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStart)
|
||||
{
|
||||
bool result = ReadPrivate(buffer, buflen, seekMsgStart);
|
||||
if (!read_ready)
|
||||
SetNotifyOnUpdate(true); // reset notification when stream is no longer "read_ready"
|
||||
return result;
|
||||
} // end NormStreamObject::Read()
|
||||
|
||||
|
||||
// Sequential (in order) read/write routines (TBD) Add a "Seek()" method
|
||||
bool NormStreamObject::ReadPrivate(char* buffer, unsigned int* buflen, bool seekMsgStart)
|
||||
{
|
||||
if (stream_closing || read_init)
|
||||
{
|
||||
|
|
@ -2856,7 +2920,6 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
return seekMsgStart ? false : true;
|
||||
}
|
||||
Retain();
|
||||
SetNotifyOnUpdate(true); // reset notification when streams are read
|
||||
unsigned int bytesRead = 0;
|
||||
unsigned int bytesToRead = *buflen;
|
||||
do
|
||||
|
|
@ -2865,6 +2928,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
if (!block)
|
||||
{
|
||||
//DMSG(0, "NormStreamObject::Read() stream buffer empty (1) (sbEmpty:%d)\n", stream_buffer.IsEmpty());
|
||||
read_ready = false;
|
||||
*buflen = bytesRead;
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
|
|
@ -2873,8 +2937,21 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((block_pool.GetCount() < block_pool_threshold) ||
|
||||
(session.ReceiverIsLowDelay() && (read_index.block < max_pending_block)))
|
||||
bool forceForward = false;
|
||||
if (block_pool.GetCount() < block_pool_threshold)
|
||||
{
|
||||
// We're starting to fill up receive stream buffer
|
||||
// with alot of partially received coding blocks
|
||||
forceForward = true;
|
||||
}
|
||||
else if (session.RcvrIsLowDelay())
|
||||
{
|
||||
// Has the sender moved forward to the next FEC blocks
|
||||
long delta = max_pending_block - read_index.block;
|
||||
if (delta > session.RcvrGetMaxDelay())
|
||||
forceForward = true;
|
||||
}
|
||||
if (forceForward)
|
||||
{
|
||||
// Force read_index forward and try again.
|
||||
if (++read_index.segment >= ndata)
|
||||
|
|
@ -2900,6 +2977,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
{
|
||||
//DMSG(0, "NormStreamObject::Read(%lu:%hu) stream buffer empty (2)\n",
|
||||
// (UINT32)read_index.block, read_index.segment);
|
||||
read_ready = false;
|
||||
*buflen = bytesRead;
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
|
|
@ -2908,8 +2986,21 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((block_pool.GetCount() < block_pool_threshold) ||
|
||||
(session.ReceiverIsLowDelay() && (read_index.block < max_pending_block)))
|
||||
bool forceForward = false;
|
||||
if (block_pool.GetCount() < block_pool_threshold)
|
||||
{
|
||||
// We're starting to fill up receive stream buffer
|
||||
// with alot of partially received coding blocks
|
||||
forceForward = true;
|
||||
}
|
||||
else if (session.RcvrIsLowDelay())
|
||||
{
|
||||
// Has the sender moved forward to the next FEC blocks
|
||||
long delta = max_pending_block - read_index.block;
|
||||
if (delta > session.RcvrGetMaxDelay())
|
||||
forceForward = true;
|
||||
}
|
||||
if (forceForward)
|
||||
{
|
||||
// Force read_index forward and try again.
|
||||
if (++read_index.segment >= ndata)
|
||||
|
|
@ -2930,7 +3021,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
}
|
||||
}
|
||||
} // end if (!segment)
|
||||
|
||||
read_ready = true;
|
||||
UINT16 length = NormDataMsg::ReadStreamPayloadLength(segment);
|
||||
if (0 == length)
|
||||
{
|
||||
|
|
@ -2958,6 +3049,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
}
|
||||
else if (length > segment_size)
|
||||
{
|
||||
// This segment is an invalid segment because its length is too long
|
||||
Release();
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
|
|
@ -2980,6 +3072,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
Prune(read_index.block, false);
|
||||
}
|
||||
*buflen = 0;
|
||||
read_ready = false; //DetermineReadReadiness();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3002,6 +3095,7 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
LocalNodeId(), (UINT16)transport_id, (UINT32)read_index.block, read_index.segment, read_offset, segmentOffset);
|
||||
read_offset = segmentOffset;
|
||||
*buflen = 0;
|
||||
read_ready = false; //DetermineReadReadiness();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3025,16 +3119,13 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
// Reset our read_offset ...
|
||||
read_offset = segmentOffset;
|
||||
*buflen = 0;
|
||||
read_ready = false; //DetermineReadReadiness();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (seekMsgStart)
|
||||
{
|
||||
/* stream payload flags have in deprecated in favor of "payload_msg_start"
|
||||
if ((0 != index) ||
|
||||
!NormDataMsg::StreamPayloadFlagIsSet(segment, NormDataMsg::FLAG_MSG_START))
|
||||
*/
|
||||
UINT16 msgStart = NormDataMsg::ReadStreamPayloadMsgStart(segment);
|
||||
if (0 == msgStart)
|
||||
{
|
||||
|
|
@ -3101,7 +3192,15 @@ bool NormStreamObject::Read(char* buffer, unsigned int* buflen, bool seekMsgStar
|
|||
read_index.block++;
|
||||
read_index.segment = 0;
|
||||
Prune(read_index.block, false);
|
||||
if (0 == bytesToRead)
|
||||
read_ready = DetermineReadReadiness();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 == bytesToRead)
|
||||
read_ready = (NULL != block->GetSegment(read_index.segment));
|
||||
}
|
||||
|
||||
if (streamEnded)
|
||||
{
|
||||
DMSG(4, "NormStreamObject::Read() stream ended by sender 2\n");
|
||||
|
|
@ -3194,8 +3293,6 @@ void NormStreamObject::Terminate()
|
|||
}
|
||||
NormDataMsg::WriteStreamPayloadOffset(segment, write_offset);
|
||||
|
||||
//NormDataMsg::ResetStreamPayloadFlags(segment); // flags are deprecated!
|
||||
//NormDataMsg::SetStreamPayloadFlag(segment, NormDataMsg::FLAG_STREAM_END);
|
||||
block->SetPending(write_index.segment);
|
||||
if (++write_index.segment >= ndata)
|
||||
{
|
||||
|
|
@ -3231,7 +3328,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
|
|||
if (deltaBlock > (block_pool.GetTotal() >> 1))
|
||||
{
|
||||
write_vacancy = false;
|
||||
DMSG(8, "NormStreamObject::Write() stream buffer full (1)\n", len, eom);
|
||||
DMSG(4, "NormStreamObject::Write() stream buffer full (1)\n");
|
||||
if (!push_mode) break;
|
||||
}
|
||||
NormBlock* block = stream_buffer.Find(write_index.block);
|
||||
|
|
@ -3263,7 +3360,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
|
|||
}
|
||||
else
|
||||
{
|
||||
DMSG(8, "NormStreamObject::Write() stream buffer full (2) len:%d eom:%d\n", len, eom);
|
||||
DMSG(4, "NormStreamObject::Write() stream buffer full (2) len:%d eom:%d\n", len, eom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3304,7 +3401,7 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
|
|||
}
|
||||
else
|
||||
{
|
||||
DMSG(8, "NormStreamObject::Write() stream buffer full (3)\n");
|
||||
DMSG(4, "NormStreamObject::Write() stream buffer full (3)\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3372,11 +3469,9 @@ UINT32 NormStreamObject::Write(const char* buffer, UINT32 len, bool eom)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (0 != nBytes)
|
||||
//if (0 != nBytes)
|
||||
session.TouchServer();
|
||||
}
|
||||
if ((0 != nBytes) && posted_tx_queue_vacancy)
|
||||
posted_tx_queue_vacancy = false;
|
||||
return nBytes;
|
||||
} // end NormStreamObject::Write()
|
||||
|
||||
|
|
@ -3473,6 +3568,33 @@ bool NormObjectTable::Init(UINT16 rangeMax, UINT16 tableSize)
|
|||
return true;
|
||||
} // end NormObjectTable::Init()
|
||||
|
||||
void NormObjectTable::SetRangeMax(UINT16 rangeMax)
|
||||
{
|
||||
if (rangeMax < range_max)
|
||||
{
|
||||
// Prune if necessary
|
||||
while (range > rangeMax)
|
||||
{
|
||||
NormObject* obj = Find(range_lo);
|
||||
ASSERT(obj);
|
||||
NormServerNode* sender = obj->GetServer();
|
||||
NormSession& session = obj->GetSession();
|
||||
if (NULL == sender)
|
||||
{
|
||||
session.Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, obj);
|
||||
session.DeleteTxObject(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!session.ClientIsSilent()) obj = Find(range_hi);
|
||||
session.Notify(NormController::RX_OBJECT_ABORTED, sender, obj);
|
||||
sender->DeleteObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
range_max = rangeMax;
|
||||
} // end NormObjectTable::SetRangeMax()
|
||||
|
||||
void NormObjectTable::Destroy()
|
||||
{
|
||||
if (table)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@ class NormObject
|
|||
|
||||
// This must be reset after each update
|
||||
void SetNotifyOnUpdate(bool state)
|
||||
{notify_on_update = state;}
|
||||
{
|
||||
notify_on_update = state;
|
||||
}
|
||||
|
||||
// Object information
|
||||
NormObject::Type GetType() const {return type;}
|
||||
|
|
@ -116,6 +118,8 @@ class NormObject
|
|||
bool IsPending(bool flush = true) const;
|
||||
bool IsRepairPending() const;
|
||||
bool IsPendingInfo() {return pending_info;}
|
||||
bool PendingMaskIsSet() const
|
||||
{return pending_mask.IsSet();}
|
||||
bool GetFirstPending(NormBlockId& blockId) const
|
||||
{
|
||||
UINT32 index = 0;
|
||||
|
|
@ -297,6 +301,8 @@ class NormFileObject : public NormObject
|
|||
result ? strncpy(path, newPath, PATH_MAX) : NULL;
|
||||
return result;
|
||||
}
|
||||
bool PadToSize()
|
||||
{return file.Pad(GetSize().GetOffset());}
|
||||
|
||||
virtual bool WriteSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
|
|
@ -404,8 +410,6 @@ class NormStreamObject : public NormObject
|
|||
bool Read(char* buffer, unsigned int* buflen, bool findMsgStart = false);
|
||||
UINT32 Write(const char* buffer, UINT32 len, bool eom = false);
|
||||
|
||||
bool SyncOffsetIsValid() {return sync_offset_valid;}
|
||||
UINT32 GetSyncOffset() {return sync_offset;}
|
||||
UINT32 GetCurrentReadOffset() {return read_offset;}
|
||||
|
||||
bool StreamUpdateStatus(NormBlockId blockId);
|
||||
|
|
@ -434,8 +438,9 @@ class NormStreamObject : public NormObject
|
|||
void Rewind();
|
||||
|
||||
bool LockBlocks(NormBlockId nextId, NormBlockId lastId);
|
||||
bool LockSegments(NormBlockId blockId, NormSegmentId firstId,
|
||||
NormSegmentId lastId);
|
||||
void UnlockBlock(NormBlockId blockId);
|
||||
|
||||
bool LockSegments(NormBlockId blockId, NormSegmentId firstId, NormSegmentId lastId);
|
||||
NormBlockId StreamBufferLo() {return stream_buffer.RangeLo();}
|
||||
void Prune(NormBlockId blockId, bool updateStatus);
|
||||
|
||||
|
|
@ -456,12 +461,19 @@ class NormStreamObject : public NormObject
|
|||
void SetBlockPoolThreshold(UINT32 value)
|
||||
{block_pool_threshold = value;}
|
||||
|
||||
void ResetPostedTxQueueVacancy() {posted_tx_queue_vacancy = false;}
|
||||
bool IsReadReady() const {return read_ready;}
|
||||
|
||||
bool DetermineReadReadiness()
|
||||
{
|
||||
NormBlock* block = stream_buffer.Find(read_index.block);
|
||||
return ((NULL != block) && (NULL != block->GetSegment(read_index.segment)));
|
||||
}
|
||||
|
||||
bool IsReadIndex(NormBlockId blockId, NormSegmentId segmentId) const
|
||||
{return ((blockId == read_index.block) && (segmentId == read_index.segment));}
|
||||
|
||||
private:
|
||||
bool ReadPrivate(char* buffer, unsigned int* buflen, bool findMsgStart = false);
|
||||
void Terminate();
|
||||
|
||||
class Index
|
||||
|
|
@ -474,8 +486,6 @@ class NormStreamObject : public NormObject
|
|||
bool stream_sync;
|
||||
NormBlockId stream_sync_id;
|
||||
NormBlockId stream_next_id;
|
||||
UINT32 sync_offset;
|
||||
bool sync_offset_valid;
|
||||
|
||||
NormBlockPool block_pool;
|
||||
NormSegmentPool segment_pool;
|
||||
|
|
@ -485,10 +495,10 @@ class NormStreamObject : public NormObject
|
|||
Index tx_index;
|
||||
UINT32 tx_offset;
|
||||
bool write_vacancy;
|
||||
bool posted_tx_queue_vacancy;
|
||||
bool read_init;
|
||||
Index read_index;
|
||||
UINT32 read_offset;
|
||||
bool read_ready;
|
||||
bool flush_pending;
|
||||
bool msg_start;
|
||||
FlushMode flush_mode;
|
||||
|
|
@ -539,9 +549,11 @@ class NormObjectTable
|
|||
NormObjectTable();
|
||||
~NormObjectTable();
|
||||
bool Init(UINT16 rangeMax, UINT16 tableSize = 256);
|
||||
void SetRangeMax(UINT16 rangeMax);
|
||||
void Destroy();
|
||||
|
||||
bool IsInited() const {return (NULL != table);}
|
||||
UINT16 GetRangeMax() const {return range_max;}
|
||||
bool CanInsert(NormObjectId objectId) const;
|
||||
bool Insert(NormObject* theObject);
|
||||
bool Remove(NormObject* theObject);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -491,8 +491,7 @@ NormObjectSize NormBlock::GetBytesPending(UINT16 numData,
|
|||
} while (GetNextPending(nextId));
|
||||
}
|
||||
// Correct for final_segment_size, if applicable
|
||||
if ((id == finalBlockId) &&
|
||||
(IsPending(numData - 1)))
|
||||
if ((id == finalBlockId) && IsPending(numData - 1))
|
||||
{
|
||||
pendingBytes -= NormObjectSize(segmentSize);
|
||||
pendingBytes += NormObjectSize(finalSegmentSize);
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@ class NormSegmentPool
|
|||
void Put(char* segment)
|
||||
{
|
||||
ASSERT(seg_count < seg_total);
|
||||
//char** ptr = (char**)segment;
|
||||
//ptr = seg_list;
|
||||
// (TBD) avoid this system call
|
||||
//memcpy(segment, &seg_list, sizeof(char*));
|
||||
*((char**)segment) = seg_list; // this might make a warning on Solaris
|
||||
seg_list = segment;
|
||||
seg_count++;
|
||||
|
|
@ -32,7 +28,6 @@ class NormSegmentPool
|
|||
unsigned long CurrentUsage() const {return (seg_total - seg_count);}
|
||||
unsigned long PeakUsage() const {return peak_usage;}
|
||||
unsigned long OverunCount() const {return overruns;}
|
||||
|
||||
unsigned int GetSegmentSize() {return seg_size;}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -13,16 +13,21 @@ const double NormSession::DEFAULT_BACKOFF_FACTOR = 4.0;
|
|||
const double NormSession::DEFAULT_GSIZE_ESTIMATE = 1000.0;
|
||||
const UINT16 NormSession::DEFAULT_NDATA = 64;
|
||||
const UINT16 NormSession::DEFAULT_NPARITY = 32;
|
||||
const UINT16 NormSession::DEFAULT_TX_CACHE_MIN = 8;
|
||||
const UINT16 NormSession::DEFAULT_TX_CACHE_MAX = 256;
|
||||
|
||||
NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
|
||||
: session_mgr(sessionMgr), notify_pending(false), tx_port(0),
|
||||
tx_socket(&tx_socket_actual), tx_socket_actual(ProtoSocket::UDP),
|
||||
tx_socket_actual(ProtoSocket::UDP), tx_socket(&tx_socket_actual),
|
||||
rx_socket(ProtoSocket::UDP), local_node_id(localNodeId),
|
||||
ttl(DEFAULT_TTL), tos(0), loopback(false), rx_port_reuse(false), rx_addr_bind(false),
|
||||
tx_rate(DEFAULT_TRANSMIT_RATE/8.0), tx_rate_min(-1.0), tx_rate_max(-1.0),
|
||||
backoff_factor(DEFAULT_BACKOFF_FACTOR), is_server(false), instance_id(0),
|
||||
ndata(DEFAULT_NDATA), nparity(DEFAULT_NPARITY), auto_parity(0), extra_parity(0),
|
||||
next_tx_object_id(0), tx_cache_count_min(8), tx_cache_count_max(256),
|
||||
sndr_emcon(false), encoder(NULL),
|
||||
next_tx_object_id(0),
|
||||
tx_cache_count_min(DEFAULT_TX_CACHE_MIN),
|
||||
tx_cache_count_max(DEFAULT_TX_CACHE_MAX),
|
||||
tx_cache_size_max((UINT32)20*1024*1024),
|
||||
flush_count(NORM_ROBUST_FACTOR+1),
|
||||
posted_tx_queue_empty(false),
|
||||
|
|
@ -36,7 +41,8 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
|
|||
grtt_decrease_delay_count(DEFAULT_GRTT_DECREASE_DELAY),
|
||||
grtt_response(false), grtt_current_peak(0.0), grtt_age(0.0),
|
||||
cc_enable(false), cc_sequence(0), cc_slow_start(true), cc_active(false),
|
||||
is_client(false), unicast_nacks(false), client_silent(false), receiver_low_delay(false),
|
||||
is_client(false), unicast_nacks(false),
|
||||
client_silent(false), rcvr_ignore_info(false), rcvr_max_delay(-1),
|
||||
default_repair_boundary(NormServerNode::BLOCK_BOUNDARY),
|
||||
default_nacking_mode(NormObject::NACK_NORMAL),
|
||||
trace(false), tx_loss_rate(0.0), rx_loss_rate(0.0),
|
||||
|
|
@ -82,6 +88,7 @@ NormSession::NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId)
|
|||
report_timer.SetRepeat(-1);
|
||||
}
|
||||
|
||||
|
||||
NormSession::~NormSession()
|
||||
{
|
||||
Close();
|
||||
|
|
@ -229,9 +236,8 @@ void NormSession::Close()
|
|||
|
||||
bool NormSession::SetMulticastInterface(const char* interfaceName)
|
||||
{
|
||||
if (interfaceName)
|
||||
if (NULL != interfaceName)
|
||||
{
|
||||
|
||||
bool result = true;
|
||||
if (rx_socket.IsOpen())
|
||||
result &= rx_socket.SetMulticastInterface(interfaceName);
|
||||
|
|
@ -358,19 +364,19 @@ bool NormSession::StartServer(UINT16 instanceId,
|
|||
if (!Open(interfaceName)) return false;
|
||||
}
|
||||
// (TBD) parameterize the object history depth
|
||||
if (!tx_table.Init(256))
|
||||
if (!tx_table.Init(tx_cache_count_max))
|
||||
{
|
||||
DMSG(0, "NormSession::StartServer() tx_table.Init() error!\n");
|
||||
StopServer();
|
||||
return false;
|
||||
}
|
||||
if (!tx_pending_mask.Init(256, 0x0000ffff))
|
||||
if (!tx_pending_mask.Init(tx_cache_count_max, 0x0000ffff))
|
||||
{
|
||||
DMSG(0, "NormSession::StartServer() tx_pending_mask.Init() error!\n");
|
||||
StopServer();
|
||||
return false;
|
||||
}
|
||||
if (!tx_repair_mask.Init(256, 0x0000ffff))
|
||||
if (!tx_repair_mask.Init(tx_cache_count_max, 0x0000ffff))
|
||||
{
|
||||
DMSG(0, "NormSession::StartServer() tx_repair_mask.Init() error!\n");
|
||||
StopServer();
|
||||
|
|
@ -407,7 +413,15 @@ bool NormSession::StartServer(UINT16 instanceId,
|
|||
|
||||
if (numParity)
|
||||
{
|
||||
if (!encoder.Init(numParity, segmentSize + NormDataMsg::GetStreamPayloadHeaderLength()))
|
||||
if (NULL != encoder) delete encoder;
|
||||
if (NULL == (encoder = new NormEncoderRS8a))
|
||||
{
|
||||
DMSG(0, "NormSession::StartServer() new NormEncoderRS8a error: %s\n", GetErrorString());
|
||||
StopServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!encoder->Init(numData, numParity, segmentSize + NormDataMsg::GetStreamPayloadHeaderLength()))
|
||||
{
|
||||
DMSG(0, "NormSession::StartServer() encoder init error\n");
|
||||
StopServer();
|
||||
|
|
@ -477,7 +491,12 @@ void NormSession::StopServer()
|
|||
repair_timer.Deactivate();
|
||||
tx_repair_pending = false;
|
||||
}
|
||||
encoder.Destroy();
|
||||
if (NULL != encoder)
|
||||
{
|
||||
encoder->Destroy();
|
||||
delete encoder;
|
||||
encoder = NULL;
|
||||
}
|
||||
acking_node_tree.Destroy();
|
||||
cc_node_list.Destroy();
|
||||
// Iterate tx_table and release objects
|
||||
|
|
@ -749,6 +768,7 @@ void NormSession::Serve()
|
|||
}
|
||||
}
|
||||
}
|
||||
//ASSERT(stream->IsPending() || stream->IsRepairPending());
|
||||
if (!posted_tx_queue_empty && stream->IsPending() && !stream->IsClosing())
|
||||
{
|
||||
//data_active = false;
|
||||
|
|
@ -778,9 +798,7 @@ void NormSession::Serve()
|
|||
{
|
||||
data_active = false; // (TBD) should we wait until the flush process completes before setting false???
|
||||
posted_tx_queue_empty = true;
|
||||
Notify(NormController::TX_QUEUE_EMPTY,
|
||||
(NormServerNode*)NULL,
|
||||
(NormObject*)NULL);
|
||||
Notify(NormController::TX_QUEUE_EMPTY, (NormServerNode*)NULL, (NormObject*)NULL);
|
||||
// (TBD) Was session deleted?
|
||||
return;
|
||||
}
|
||||
|
|
@ -1103,7 +1121,8 @@ void NormSession::QueueMessage(NormMsg* msg)
|
|||
tx_timer.SetInterval(0.0);
|
||||
ActivateTimer(tx_timer);
|
||||
}
|
||||
if (msg) message_queue.Append(msg);
|
||||
if (msg)
|
||||
message_queue.Append(msg);
|
||||
} // end NormSesssion::QueueMessage(NormMsg& msg)
|
||||
|
||||
|
||||
|
|
@ -1339,29 +1358,52 @@ void NormSession::DeleteTxObject(NormObject* obj)
|
|||
obj->Release();
|
||||
} // end NormSession::DeleteTxObject()
|
||||
|
||||
void NormSession::SetTxCacheBounds(NormObjectSize sizeMax,
|
||||
bool NormSession::SetTxCacheBounds(NormObjectSize sizeMax,
|
||||
unsigned long countMin,
|
||||
unsigned long countMax)
|
||||
{
|
||||
bool result = true;
|
||||
tx_cache_size_max = sizeMax;
|
||||
tx_cache_count_min = (countMin < countMax) ? countMin : countMax;
|
||||
tx_cache_count_max = (countMax > countMin) ? countMax : countMin;
|
||||
// Now trim the tx_table as needed
|
||||
|
||||
if (IsServer())
|
||||
{
|
||||
// Trim/resize the tx_table and tx masks as needed
|
||||
unsigned long count = tx_table.Count();
|
||||
while ((count >= tx_cache_count_min) &&
|
||||
((count >= tx_cache_count_max) ||
|
||||
(tx_table.GetSize() > tx_cache_size_max)))
|
||||
{
|
||||
// Remove oldest non-pending
|
||||
// Remove oldest (hopefully non-pending ) object
|
||||
NormObject* oldest = tx_table.Find(tx_table.RangeLo());
|
||||
ASSERT(oldest);
|
||||
Notify(NormController::TX_OBJECT_PURGED, (NormServerNode*)NULL, oldest);
|
||||
DeleteTxObject(oldest);
|
||||
count = tx_table.Count();
|
||||
}
|
||||
if (tx_cache_count_max < DEFAULT_TX_CACHE_MAX)
|
||||
countMax = DEFAULT_TX_CACHE_MAX;
|
||||
else
|
||||
countMax = tx_cache_count_max;
|
||||
if (countMax != tx_table.GetRangeMax())
|
||||
{
|
||||
tx_table.SetRangeMax((UINT16)countMax);
|
||||
result = tx_pending_mask.Resize(countMax);
|
||||
result &= tx_repair_mask.Resize(countMax);
|
||||
if (!result)
|
||||
{
|
||||
countMax = tx_pending_mask.GetSize();
|
||||
if (tx_repair_mask.GetSize() < (INT32)countMax)
|
||||
countMax = tx_repair_mask.GetSize();
|
||||
if (tx_cache_count_max > countMax)
|
||||
tx_cache_count_max = countMax;
|
||||
if (tx_cache_count_min > tx_cache_count_max)
|
||||
tx_cache_count_min = tx_cache_count_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} // end NormSession::SetTxCacheBounds()
|
||||
|
||||
NormBlock* NormSession::ServerGetFreeBlock(NormObjectId objectId,
|
||||
|
|
@ -1550,14 +1592,10 @@ void NormTrace(const struct timeval& currentTime,
|
|||
if (data.IsData() && data.IsStream())
|
||||
{
|
||||
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_MSG_START))
|
||||
if (0 != NormDataMsg::ReadStreamPayloadMsgStart(data.GetPayload()))
|
||||
UINT16 msgStartOffset = NormDataMsg::ReadStreamPayloadMsgStart(data.GetPayload());
|
||||
if (0 != msgStartOffset)
|
||||
{
|
||||
// (TBD) use "payload_msg_start" value for this ...
|
||||
// We usually use the first two bytes of "messages"
|
||||
// as a "message length" header
|
||||
UINT16 x;
|
||||
memcpy(&x, data.GetPayloadData(), 2);
|
||||
DMSG(0, "start word>%hu ", ntohs(x));
|
||||
DMSG(0, "start word>%hu ", msgStartOffset - 1);
|
||||
}
|
||||
//if (NormDataMsg::StreamPayloadFlagIsSet(data.GetPayload(), NormDataMsg::FLAG_STREAM_END))
|
||||
if (0 == NormDataMsg::ReadStreamPayloadLength(data.GetPayload()))
|
||||
|
|
@ -1923,13 +1961,11 @@ void NormSession::ServerHandleCCFeedback(struct timeval currentTime,
|
|||
NormCCNode* node = (NormCCNode*)cc_node_list.FindNodeById(nodeId);
|
||||
if (node) ccRtt = node->UpdateRtt(ccRtt);
|
||||
|
||||
if (0 == (ccFlags & NormCC::START))
|
||||
{
|
||||
// slow start has ended
|
||||
cc_slow_start = false;
|
||||
// adjust rate using current rtt for node
|
||||
bool ccSlowStart = (0 != (ccFlags & NormCC::START));
|
||||
|
||||
if (!ccSlowStart)
|
||||
ccRate = CalculateRate(nominal_packet_size, ccRtt, ccLoss);
|
||||
}
|
||||
|
||||
//DMSG(0, "NormSession::ServerHandleCCFeedback() node>%lu rate>%lf (rtt>%lf loss>%lf slow_start>%d)\n",
|
||||
// nodeId, ccRate * 8.0 / 1000.0, ccRtt, ccLoss, (0 != (ccFlags & NormCC::START)));
|
||||
|
||||
|
|
@ -1959,6 +1995,7 @@ void NormSession::ServerHandleCCFeedback(struct timeval currentTime,
|
|||
next->SetCCSequence(ccSequence);
|
||||
next->SetActive(true);
|
||||
next->SetFeedbackTime(currentTime);
|
||||
cc_slow_start = ccSlowStart; // use CLR status for our slow_start state
|
||||
if (savedId == nodeId)
|
||||
{
|
||||
// This was feedback from the current CLR
|
||||
|
|
@ -2559,6 +2596,7 @@ void NormSession::ServerHandleNackMessage(const struct timeval& currentTime, Nor
|
|||
} // end if (freshBlock)
|
||||
ASSERT(NULL != block);
|
||||
// If stream && explicit data repair, lock the data for retransmission
|
||||
// (TBD) this use of "ndata" needs to be replaced for dynamically shortened blocks
|
||||
if (object->IsStream() && (nextSegmentId < ndata))
|
||||
{
|
||||
bool attemptLock = true;
|
||||
|
|
@ -3103,9 +3141,18 @@ bool NormSession::OnTxTimeout(ProtoTimer& /*theTimer*/)
|
|||
}
|
||||
else
|
||||
{
|
||||
// (TBD) should we check the type of error that occurred
|
||||
// and take some smarter action here (e.g. re-open our sockets?)
|
||||
// Requeue the message for another try
|
||||
if (!advertise_repairs)
|
||||
message_queue.Prepend(msg);
|
||||
// Make sure the tx_timer interval is non-ZERO
|
||||
// (this avoids a sort of infinite loop that can occur
|
||||
// under certain conditions)
|
||||
if (tx_rate > 0.0)
|
||||
tx_timer.SetInterval(msg->GetLength() / tx_rate);
|
||||
else if (0.0 == tx_timer.GetInterval())
|
||||
tx_timer.SetInterval(0.1);
|
||||
}
|
||||
return true; // reinstall tx_timer
|
||||
}
|
||||
|
|
@ -3203,12 +3250,17 @@ bool NormSession::SendMessage(NormMsg& msg)
|
|||
|
||||
msg.SetSourceId(local_node_id);
|
||||
UINT16 msgSize = msg.GetLength();
|
||||
bool result = true;
|
||||
// Possibly drop some tx messages for testing purposes
|
||||
bool drop = (UniformRand(100.0) < tx_loss_rate);
|
||||
if (drop || (isClientMsg && client_silent))
|
||||
{
|
||||
//DMSG(0, "TX MESSAGE DROPPED! (tx_loss_rate:%lf\n", tx_loss_rate);
|
||||
if (!(isClientMsg && client_silent))
|
||||
{
|
||||
// Update sent rate tracker even if dropped (for testing/debugging)
|
||||
sent_accumulator += msgSize;
|
||||
nominal_packet_size += 0.05 * (((double)msgSize) - nominal_packet_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3228,40 +3280,34 @@ bool NormSession::SendMessage(NormMsg& msg)
|
|||
}
|
||||
else
|
||||
{
|
||||
DMSG(8, "NormSession::SendMessage() sendto() error\n");
|
||||
result = false;
|
||||
DMSG(8, "NormSession::SendMessage() sendto() error: %s\n", GetErrorString());
|
||||
tx_sequence--;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
if (isProbe)
|
||||
{
|
||||
if (isProbe)
|
||||
probe_pending = false;
|
||||
probe_data_check = true;
|
||||
if (probe_reset)
|
||||
{
|
||||
probe_pending = false;
|
||||
probe_data_check = true;
|
||||
if (probe_reset)
|
||||
{
|
||||
probe_reset = false;
|
||||
if (!probe_timer.IsActive())
|
||||
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()
|
||||
|
||||
void NormSession::SetGrttProbingInterval(double intervalMin, double intervalMax)
|
||||
|
|
@ -3503,7 +3549,7 @@ bool NormSession::OnProbeTimeout(ProtoTimer& /*theTimer*/)
|
|||
feedbackAge += 1.0e-06*((double)(currentTime.tv_usec - feedbackTime.tv_usec));
|
||||
else
|
||||
feedbackAge -= 1.0e-06*((double)(feedbackTime.tv_usec - currentTime.tv_usec));
|
||||
double maxFeedbackAge = 5 * grtt_advertised;
|
||||
double maxFeedbackAge = 5 * MAX(grtt_advertised, next->GetRtt());
|
||||
// Safety bound to compensate for computer clock coarseness
|
||||
// and possible sluggish feedback from slower machines
|
||||
// at higher norm data rates (keeps rate from being
|
||||
|
|
@ -3511,7 +3557,11 @@ bool NormSession::OnProbeTimeout(ProtoTimer& /*theTimer*/)
|
|||
if (maxFeedbackAge <(10*NORM_TICK_MIN))
|
||||
maxFeedbackAge = (10*NORM_TICK_MIN);
|
||||
if (feedbackAge > maxFeedbackAge)
|
||||
{
|
||||
DMSG(6, "Deactivating cc node feedbackAge:%lf sec maxAge:%lf sec\n",
|
||||
feedbackAge, maxFeedbackAge);
|
||||
next->SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ class NormSession
|
|||
static const double DEFAULT_GSIZE_ESTIMATE;
|
||||
static const UINT16 DEFAULT_NDATA;
|
||||
static const UINT16 DEFAULT_NPARITY;
|
||||
static const UINT16 DEFAULT_TX_CACHE_MIN;
|
||||
static const UINT16 DEFAULT_TX_CACHE_MAX;
|
||||
|
||||
enum ProbingMode {PROBE_NONE, PROBE_PASSIVE, PROBE_ACTIVE};
|
||||
enum AckingStatus
|
||||
{
|
||||
|
|
@ -129,7 +132,7 @@ class NormSession
|
|||
// (TBD) call tx_socket->SetFlowLabel() to set traffic class for IPv6 sockets
|
||||
// (or should we have ProtoSocket::SetTOS() do this for us?)
|
||||
bool result = tx_socket->IsOpen() ? tx_socket->SetTOS(theTOS) : true;
|
||||
tos = result ? theTOS : ttl;
|
||||
tos = result ? theTOS : tos;
|
||||
return result;
|
||||
}
|
||||
bool SetLoopback(bool state)
|
||||
|
|
@ -177,7 +180,7 @@ class NormSession
|
|||
void SetGrttMax(double grttMax) {grtt_max = grttMax;}
|
||||
|
||||
void SetTxRateBounds(double rateMin, double rateMax);
|
||||
void SetTxCacheBounds(NormObjectSize sizeMax,
|
||||
bool SetTxCacheBounds(NormObjectSize sizeMax,
|
||||
unsigned long countMin,
|
||||
unsigned long countMax);
|
||||
void Notify(NormController::Event event,
|
||||
|
|
@ -247,6 +250,13 @@ class NormSession
|
|||
void ServerSetExtraParity(UINT16 extraParity)
|
||||
{extra_parity = extraParity;}
|
||||
|
||||
// EMCON Server (useful when there are silent receivers)
|
||||
// (NORM_INFO is redundantly sent)
|
||||
void SndrSetEmcon(bool state)
|
||||
{sndr_emcon = true;}
|
||||
bool SndrEmcon() const
|
||||
{return sndr_emcon;}
|
||||
|
||||
bool ServerGetFirstPending(NormObjectId& objectId)
|
||||
{
|
||||
UINT32 index;
|
||||
|
|
@ -282,7 +292,7 @@ class NormSession
|
|||
}
|
||||
|
||||
void ServerEncode(const char* segment, char** parityVectorList)
|
||||
{encoder.Encode(segment, parityVectorList);}
|
||||
{encoder->Encode(segment, parityVectorList);}
|
||||
|
||||
|
||||
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
|
||||
|
|
@ -313,16 +323,30 @@ class NormSession
|
|||
{return remote_server_buffer_size;}
|
||||
void ClientSetUnicastNacks(bool state) {unicast_nacks = state;}
|
||||
bool ClientGetUnicastNacks() const {return unicast_nacks;}
|
||||
|
||||
void ClientSetSilent(bool state)
|
||||
{
|
||||
client_silent = state;
|
||||
receiver_low_delay = client_silent ? receiver_low_delay : false;
|
||||
}
|
||||
{client_silent = state;}
|
||||
bool ClientIsSilent() const {return client_silent;}
|
||||
|
||||
void ReceiverSetLowDelay(bool state)
|
||||
{receiver_low_delay = client_silent ? state : false;}
|
||||
bool ReceiverIsLowDelay() {return receiver_low_delay;}
|
||||
void RcvrSetIgnoreInfo(bool state)
|
||||
{rcvr_ignore_info = state;}
|
||||
bool RcvrIgnoreInfo() const
|
||||
{return rcvr_ignore_info;}
|
||||
|
||||
// "-1" corresponds to typical operation where source data for
|
||||
// partially received FEC blocks are only provided to the app
|
||||
// when buffer constraints require it.
|
||||
// Otherwise, the "maxDelay" corresponds to the max number
|
||||
// of FEC blocks the receiver waits before passing partially
|
||||
// received blocks to the app.
|
||||
// Note a "maxDelay == 0" provides _no_ protection from
|
||||
// out-of-order received packets!
|
||||
void RcvrSetMaxDelay(INT32 maxDelay)
|
||||
{rcvr_max_delay = maxDelay;}
|
||||
bool RcvrIsLowDelay()
|
||||
{return (ClientIsSilent() && (rcvr_max_delay >= 0));}
|
||||
INT32 RcvrGetMaxDelay() const
|
||||
{return rcvr_max_delay;}
|
||||
|
||||
NormObject::NackingMode ClientGetDefaultNackingMode() const
|
||||
{return default_nacking_mode;}
|
||||
|
|
@ -401,8 +425,8 @@ class NormSession
|
|||
bool notify_pending;
|
||||
ProtoTimer tx_timer;
|
||||
UINT16 tx_port;
|
||||
ProtoSocket* tx_socket;
|
||||
ProtoSocket tx_socket_actual;
|
||||
ProtoSocket* tx_socket;
|
||||
ProtoSocket rx_socket;
|
||||
NormMessageQueue message_queue;
|
||||
NormMessageQueue message_pool;
|
||||
|
|
@ -431,6 +455,7 @@ class NormSession
|
|||
UINT16 nparity;
|
||||
UINT16 auto_parity;
|
||||
UINT16 extra_parity;
|
||||
bool sndr_emcon;
|
||||
|
||||
NormObjectTable tx_table;
|
||||
ProtoSlidingMask tx_pending_mask;
|
||||
|
|
@ -438,7 +463,7 @@ class NormSession
|
|||
ProtoTimer repair_timer;
|
||||
NormBlockPool block_pool;
|
||||
NormSegmentPool segment_pool;
|
||||
NormEncoder encoder;
|
||||
NormEncoder* encoder;
|
||||
|
||||
NormObjectId next_tx_object_id;
|
||||
unsigned int tx_cache_count_min;
|
||||
|
|
@ -507,7 +532,8 @@ class NormSession
|
|||
unsigned long remote_server_buffer_size;
|
||||
bool unicast_nacks;
|
||||
bool client_silent;
|
||||
bool receiver_low_delay;
|
||||
bool rcvr_ignore_info;
|
||||
INT32 rcvr_max_delay;
|
||||
NormServerNode::RepairBoundary default_repair_boundary;
|
||||
NormObject::NackingMode default_nacking_mode;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "normSimAgent.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
// This NORM simulation agent includes support for providing transport
|
||||
|
|
@ -8,22 +7,22 @@
|
|||
|
||||
NormSimAgent::NormSimAgent(ProtoTimerMgr& timerMgr,
|
||||
ProtoSocket::Notifier& socketNotifier)
|
||||
: session_mgr(timerMgr, socketNotifier), session(NULL),
|
||||
address(NULL), port(0), ttl(3),
|
||||
tx_rate(NormSession::DEFAULT_TRANSMIT_RATE),
|
||||
cc_enable(false), unicast_nacks(false), silent_client(false),
|
||||
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
|
||||
segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0),
|
||||
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
|
||||
grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE),
|
||||
tx_buffer_size(1024*1024), rx_buffer_size(1024*1024),
|
||||
tx_object_size(0), tx_object_interval(0.0),
|
||||
tx_object_size_min(0), tx_object_size_max(0),
|
||||
tx_repeat_count(0), tx_repeat_interval(0.0),
|
||||
stream(NULL), auto_stream(false), push_mode(false),
|
||||
flush_mode(NormStreamObject::FLUSH_PASSIVE),
|
||||
mgen(NULL), msg_sync(false), mgen_bytes(0), mgen_pending_bytes(0),
|
||||
tracing(false), tx_loss(0.0), rx_loss(0.0)
|
||||
: msg_sink(NULL), session_mgr(timerMgr, socketNotifier), session(NULL),
|
||||
address(NULL), port(0), ttl(3),
|
||||
tx_rate(NormSession::DEFAULT_TRANSMIT_RATE),
|
||||
cc_enable(false), unicast_nacks(false), silent_client(false),
|
||||
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
|
||||
segment_size(1024), ndata(32), nparity(16), auto_parity(0), extra_parity(0),
|
||||
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
|
||||
grtt_estimate(NormSession::DEFAULT_GRTT_ESTIMATE),
|
||||
tx_buffer_size(1024*1024), rx_buffer_size(1024*1024),
|
||||
tx_object_size(0), tx_object_interval(0.0),
|
||||
tx_object_size_min(0), tx_object_size_max(0),
|
||||
tx_repeat_count(0), tx_repeat_interval(0.0),
|
||||
stream(NULL), auto_stream(false), push_mode(false),
|
||||
flush_mode(NormStreamObject::FLUSH_PASSIVE),
|
||||
msg_sync(false), mgen_bytes(0), mgen_pending_bytes(0),
|
||||
tracing(false), tx_loss(0.0), rx_loss(0.0)
|
||||
{
|
||||
// Bind NormSessionMgr to this agent and simulation environment
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} // end NormSimAgent::SendMessage()
|
||||
|
||||
void NormSimAgent::OnInputReady()
|
||||
|
|
@ -622,246 +623,242 @@ bool NormSimAgent::FlushStream()
|
|||
|
||||
|
||||
void NormSimAgent::Notify(NormController::Event event,
|
||||
class NormSessionMgr* sessionMgr,
|
||||
class NormSession* session,
|
||||
class NormServerNode* server,
|
||||
class NormObject* object)
|
||||
class NormSessionMgr* sessionMgr,
|
||||
class NormSession* session,
|
||||
class NormServerNode* server,
|
||||
class NormObject* object)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
//case TX_QUEUE_VACANCY:
|
||||
case TX_QUEUE_EMPTY:
|
||||
// Can queue a new object or write to stream for transmission
|
||||
if (object && (object == stream))
|
||||
case TX_QUEUE_EMPTY:
|
||||
// Can queue a new object or write to stream for transmission
|
||||
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)
|
||||
{
|
||||
// sending a dummy byte stream
|
||||
char buffer[NormMsg::MAX_SIZE];
|
||||
stream->Write(buffer, segment_size, false);
|
||||
}
|
||||
NormObjectSize size;
|
||||
if (silent_client)
|
||||
size = NormObjectSize(rx_buffer_size);
|
||||
else
|
||||
size = object->GetSize();
|
||||
if (!((NormStreamObject*)object)->Accept(size.LSB()))
|
||||
{
|
||||
// Stream starved, ask for input from "source" ?
|
||||
OnInputReady();
|
||||
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) stream object accept error!\n");
|
||||
}
|
||||
if (!stream)
|
||||
stream = (NormStreamObject*)object;
|
||||
else
|
||||
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) warning! one stream already accepted.\n");
|
||||
}
|
||||
else
|
||||
break;
|
||||
case NormObject::FILE:
|
||||
{
|
||||
// Schedule or queue next "sim file" transmission
|
||||
if (interval_timer.GetInterval() > 0.0)
|
||||
if (!((NormSimObject*)object)->Accept())
|
||||
{
|
||||
ActivateTimer(interval_timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnIntervalTimeout(interval_timer);
|
||||
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_NEW:
|
||||
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
|
||||
switch (object->GetType())
|
||||
// Read the stream when it's updated
|
||||
if (msg_sink)
|
||||
{
|
||||
case NormObject::STREAM:
|
||||
bool dataReady = true;
|
||||
while (dataReady)
|
||||
{
|
||||
NormObjectSize size;
|
||||
if (silent_client)
|
||||
size = NormObjectSize(rx_buffer_size);
|
||||
else
|
||||
size = object->GetSize();
|
||||
if (!((NormStreamObject*)object)->Accept(size.LSB()))
|
||||
if (!mgen_pending_bytes)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) stream object accept error!\n");
|
||||
}
|
||||
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;
|
||||
// Read 2 byte MGEN msg len header
|
||||
unsigned int want = 2 - mgen_bytes;
|
||||
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
|
||||
if (got != want) break;
|
||||
mgen_pending_bytes -= got;
|
||||
mgen_bytes += got;
|
||||
if (got != want) dataReady = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
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;
|
||||
|
||||
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())
|
||||
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:
|
||||
//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;
|
||||
char buffer[1024];
|
||||
unsigned int want = 1024;
|
||||
unsigned int got = want;
|
||||
while (1)
|
||||
{
|
||||
if ((static_cast<NormStreamObject*>(object))->Read(buffer, &got))
|
||||
{
|
||||
// Break when data is no longer available
|
||||
if (got != want) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormSimAgent::Notify() detected stream break\n");
|
||||
}
|
||||
got = want = 1024;
|
||||
}
|
||||
}
|
||||
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 NormSimAgent::Notify()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#ifndef _NORM_SIM_AGENT
|
||||
#define _NORM_SIM_AGENT
|
||||
|
||||
// normSimAgent.h - Generic (base class) NORM simulation agent
|
||||
|
||||
#include "normSession.h"
|
||||
#include "protokit.h"
|
||||
|
||||
#include "mgen.h" // for MGEN instance attachment
|
||||
#include "protoSimAgent.h"
|
||||
|
||||
// Base class for Norm simulation agents (e.g. ns-2, OPNET, etc)
|
||||
class NormSimAgent : public NormController
|
||||
class NormSimAgent : public NormController, public ProtoMessageSink
|
||||
{
|
||||
public:
|
||||
virtual ~NormSimAgent();
|
||||
|
|
@ -20,8 +21,8 @@ class NormSimAgent : public NormController
|
|||
bool IsActive() {return (NULL != session);}
|
||||
void Stop();
|
||||
|
||||
bool SendMessage(unsigned int len, const char* txBuffer);
|
||||
void AttachMgen(Mgen* mgenInstance) {mgen = mgenInstance;}
|
||||
|
||||
bool SendMessage(unsigned int len, const char* txBuffer);
|
||||
|
||||
protected:
|
||||
NormSimAgent(ProtoTimerMgr& timerMgr,
|
||||
|
|
@ -29,10 +30,14 @@ class NormSimAgent : public NormController
|
|||
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
|
||||
CmdType CommandType(const char* cmd);
|
||||
virtual unsigned long GetAgentId() = 0;
|
||||
/* JPH 4/11/06 Use packet stream instead of direct call on mgen process */
|
||||
void HandleMgenMessage(char* buffer,
|
||||
unsigned int len,
|
||||
const ProtoAddress& srcAddr);
|
||||
ProtoMessageSink* msg_sink;
|
||||
|
||||
#ifdef OPNET
|
||||
void HandleMessage(char* buffer,
|
||||
unsigned int len,
|
||||
const ProtoAddress& srcAddr);
|
||||
void SetSink(ProtoMessageSink* sink){msg_sink=sink;}
|
||||
#endif //OPNET
|
||||
|
||||
private:
|
||||
void OnInputReady();
|
||||
|
|
@ -87,12 +92,10 @@ class NormSimAgent : public NormController
|
|||
char* tx_msg_buffer;
|
||||
unsigned int tx_msg_len;
|
||||
unsigned int tx_msg_index;
|
||||
Mgen* mgen; // mgen receiver
|
||||
char mgen_buffer[64];
|
||||
bool msg_sync;
|
||||
unsigned int mgen_bytes;
|
||||
unsigned int mgen_pending_bytes;
|
||||
|
||||
ProtoTimer interval_timer;
|
||||
|
||||
// protocol debug parameters
|
||||
|
|
@ -102,3 +105,4 @@ class NormSimAgent : public NormController
|
|||
|
||||
}; // end class NormSimAgent
|
||||
|
||||
#endif // NORM_SIM_AGENT
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
NormSetGrttEstimate(session, 0.001); // 1 msec initial grtt
|
||||
|
||||
NormSetTransmitRate(session, 1.0e+05); // in bits/second
|
||||
NormSetTransmitRate(session, 1.0e+07); // in bits/second
|
||||
|
||||
// Uncomment to enable TCP-friendly congestion control (overrides NormSetTransmitRate())
|
||||
//NormSetCongestionControl(session, true);
|
||||
|
|
@ -114,7 +114,7 @@ int main(int argc, char* argv[])
|
|||
//NormSetSilentReceiver(session, true);
|
||||
|
||||
// Uncomment this line to participate as a receiver
|
||||
NormStartReceiver(session, 1024*1024);
|
||||
//NormStartReceiver(session, 1024*1024);
|
||||
|
||||
// Uncomment to set large rx socket buffer size
|
||||
// (might be needed for high rate sessions)
|
||||
|
|
@ -124,7 +124,7 @@ int main(int argc, char* argv[])
|
|||
NormSessionId sessionId = (NormSessionId)rand();
|
||||
|
||||
// Uncomment the following line to start sender
|
||||
//NormStartSender(session, sessionId, 1024*1024, 1400, 64, 8);
|
||||
NormStartSender(session, sessionId, 1024*1024, 1400, 64, 8);
|
||||
|
||||
//NormSetAutoParity(session, 6);
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
|
||||
// Uncomment this line to send a stream instead of the file
|
||||
//stream = NormStreamOpen(session, 4*1024*1024);
|
||||
stream = NormStreamOpen(session, 4*1024*1024);
|
||||
|
||||
// NORM_FLUSH_PASSIVE automatically flushes full writes to
|
||||
// the stream.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
@ -32,6 +32,6 @@
|
|||
|
||||
#ifndef _NORM_VERSION
|
||||
#define _NORM_VERSION
|
||||
#define VERSION "1.3b9"
|
||||
#define VERSION "1.4b2"
|
||||
#endif // _NORM_VERSION
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* AUTHORIZATION TO USE AND DISTRIBUTE
|
||||
* Authorization TO USE AND DISTRIBUTE
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that:
|
||||
|
|
@ -33,8 +33,8 @@
|
|||
#include "ip.h" // for hdr_ip def
|
||||
#include "flags.h" // for hdr_flags def
|
||||
|
||||
#include "nsMgenAgent.h"
|
||||
#include "nsNormAgent.h"
|
||||
|
||||
static class NsNormAgentClass : public TclClass
|
||||
{
|
||||
public:
|
||||
|
|
@ -45,7 +45,7 @@ static class NsNormAgentClass : public TclClass
|
|||
|
||||
|
||||
NsNormAgent::NsNormAgent()
|
||||
: NormSimAgent(GetTimerMgr(), GetSocketNotifier())
|
||||
: NormSimAgent(GetTimerMgr(), GetSocketNotifier())
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ NsNormAgent::~NsNormAgent()
|
|||
|
||||
bool NsNormAgent::OnStartup(int argc, const char*const* argv)
|
||||
{
|
||||
|
||||
if (ProcessCommands(argc, argv))
|
||||
{
|
||||
return true;
|
||||
|
|
@ -89,10 +90,10 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
|
|||
return false;
|
||||
}
|
||||
Tcl& tcl = Tcl::instance();
|
||||
NsMgenAgent* mgenAgent = dynamic_cast<NsMgenAgent*> (tcl.lookup(argv[i]));
|
||||
if (mgenAgent)
|
||||
msg_sink = dynamic_cast<ProtoMessageSink*>(tcl.lookup(argv[i]));
|
||||
|
||||
if (msg_sink)
|
||||
{
|
||||
AttachMgen(mgenAgent->GetMgenInstance());
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -145,13 +146,3 @@ bool NsNormAgent::ProcessCommands(int argc, const char*const* argv)
|
|||
return true;
|
||||
} // end NsNormAgent::ProcessCommands()
|
||||
|
||||
bool NsNormAgent::SendMgenMessage(const char* txBuffer,
|
||||
unsigned int len,
|
||||
const ProtoAddress& /*dstAddr*/)
|
||||
{
|
||||
return SendMessage(len, txBuffer);
|
||||
} // end NsNormAgent::SendMgenMessage()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,31 +36,31 @@
|
|||
#include "nsProtoSimAgent.h" // from Protolib
|
||||
#include "normSimAgent.h"
|
||||
|
||||
#include "nsMgenAgent.h"
|
||||
|
||||
// The "NsProtoAgent" is based on the ns Agent class.
|
||||
// This lets us have a send/recv attachment to the NS
|
||||
// simulation environment
|
||||
|
||||
// IMPORTANT NOTE! NsProtoAgent must be listed _first_ here
|
||||
// (because we can't dynamic_cast install_data void* pointers)
|
||||
class NsNormAgent : public NsProtoSimAgent, public NormSimAgent, public MgenSink
|
||||
class NsNormAgent : public NsProtoSimAgent, public NormSimAgent
|
||||
{
|
||||
public:
|
||||
NsNormAgent();
|
||||
~NsNormAgent();
|
||||
public:
|
||||
NsNormAgent();
|
||||
~NsNormAgent();
|
||||
|
||||
// NsProtoSimAgent base class overrides
|
||||
virtual bool OnStartup(int argc, const char*const* argv);
|
||||
virtual bool ProcessCommands(int argc, const char*const* argv);
|
||||
virtual void OnShutdown();
|
||||
|
||||
// NormSimAgent overrides
|
||||
unsigned long GetAgentId() {return (unsigned long)addr();}
|
||||
bool HandleMessage(const char* txBuffer, unsigned int len, const ProtoAddress& srcAddr)
|
||||
{
|
||||
return NormSimAgent::SendMessage(len,txBuffer);
|
||||
}
|
||||
|
||||
// NsProtoSimAgent base class overrides
|
||||
virtual bool OnStartup(int argc, const char*const* argv);
|
||||
virtual bool ProcessCommands(int argc, const char*const* argv);
|
||||
virtual void OnShutdown();
|
||||
|
||||
// NormSimAgent override
|
||||
unsigned long GetAgentId() {return (unsigned long)addr();}
|
||||
// MgenSink override
|
||||
bool SendMgenMessage(const char* txBuffer,
|
||||
unsigned int len,
|
||||
const ProtoAddress& dstAddr);
|
||||
}; // end class NsNormAgent
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* Process model C++ form file: norm_protolib.pr.cpp */
|
||||
/* Portions of this file copyright 1992-2004 by OPNET Technologies, Inc. */
|
||||
/* Portions of this file copyright 1992-2006 by OPNET Technologies, Inc. */
|
||||
|
||||
|
||||
|
||||
/* This variable carries the header into the object file */
|
||||
const char norm_protolib_pr_cpp [] = "MIL_3_Tfile_Hdr_ 115A 30A op_runsim 7 448841E4 448841E4 1 apocalypse Jim@Hauser 0 0 none none 0 0 none 0 0 0 0 0 0 0 0 d2c 2 ";
|
||||
const char norm_protolib_pr_cpp [] = "MIL_3_Tfile_Hdr_ 115A 30A op_runsim 7 44EC8462 44EC8462 1 apocalypse Jim@Hauser 0 0 none none 0 0 none 0 0 0 0 0 0 0 0 d50 3 ";
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ class norm_protolib_state
|
|||
IpT_Address dest_ip_addr ;
|
||||
FILE* script_fp ;
|
||||
int source ;
|
||||
Ici* app_ici_v3_ptr ;
|
||||
Ici* app_ici_ptr ;
|
||||
|
||||
/* FSM code */
|
||||
void norm_protolib (OP_SIM_CONTEXT_ARG_OPT);
|
||||
|
|
@ -188,7 +188,7 @@ VosT_Obtype norm_protolib_state::obtype = (VosT_Obtype)OPC_NIL;
|
|||
#define dest_ip_addr pr_state_ptr->dest_ip_addr
|
||||
#define script_fp pr_state_ptr->script_fp
|
||||
#define source pr_state_ptr->source
|
||||
#define app_ici_v3_ptr pr_state_ptr->app_ici_v3_ptr
|
||||
#define app_ici_ptr pr_state_ptr->app_ici_ptr
|
||||
|
||||
/* These macro definitions will define a local variable called */
|
||||
/* "op_sv_ptr" in each function containing a FIN statement. */
|
||||
|
|
@ -781,11 +781,11 @@ void NormSimAgent::HandleMgenMessage(char* buffer,
|
|||
op_prg_mem_copy_create, op_prg_mem_free, buflen);
|
||||
|
||||
|
||||
op_ici_attr_set(app_ici_v3_ptr, "local_port", local_port);
|
||||
op_ici_attr_set(app_ici_v3_ptr, "rem_port", srcAddr.GetPort());
|
||||
op_ici_attr_set(app_ici_v3_ptr, "rem_addr", srcAddr.SimGetAddress());
|
||||
op_ici_attr_set(app_ici_v3_ptr, "src_addr", srcAddr.SimGetAddress());
|
||||
op_ici_install(app_ici_v3_ptr);
|
||||
op_ici_attr_set(app_ici_ptr, "local_port", local_port);
|
||||
op_ici_attr_set(app_ici_ptr, "rem_port", srcAddr.GetPort());
|
||||
op_ici_attr_set(app_ici_ptr, "rem_addr", srcAddr.SimGetAddress());
|
||||
op_ici_attr_set(app_ici_ptr, "src_addr", srcAddr.SimGetAddress());
|
||||
op_ici_install(app_ici_ptr);
|
||||
|
||||
op_pk_send_forced(pkt, OUTSTRM_TO_MGEN);
|
||||
|
||||
|
|
@ -851,7 +851,7 @@ void NormSimAgent::HandleMgenMessage(char* buffer,
|
|||
#undef dest_ip_addr
|
||||
#undef script_fp
|
||||
#undef source
|
||||
#undef app_ici_v3_ptr
|
||||
#undef app_ici_ptr
|
||||
|
||||
/* Access from C kernel using C linkage */
|
||||
extern "C"
|
||||
|
|
@ -976,7 +976,7 @@ norm_protolib_state::norm_protolib (OP_SIM_CONTEXT_ARG_OPT)
|
|||
FSM_STATE_ENTER_UNFORCED_NOLABEL (1, "init", "norm_protolib [init enter execs]")
|
||||
FSM_PROFILE_SECTION_IN ("norm_protolib [init enter execs]", state1_enter_exec)
|
||||
{
|
||||
app_ici_v3_ptr = op_ici_create ("udp_command_v3");
|
||||
app_ici_ptr = op_ici_create ("sink_command");
|
||||
|
||||
/* Obtain the object ID of the surrounding norm processor. */
|
||||
my_id = op_id_self ();
|
||||
|
|
@ -1528,9 +1528,9 @@ _op_norm_protolib_svar (void * gen_ptr, const char * var_name, void ** var_p_ptr
|
|||
*var_p_ptr = (void *) (&prs_ptr->source);
|
||||
FOUT
|
||||
}
|
||||
if (strcmp ("app_ici_v3_ptr" , var_name) == 0)
|
||||
if (strcmp ("app_ici_ptr" , var_name) == 0)
|
||||
{
|
||||
*var_p_ptr = (void *) (&prs_ptr->app_ici_v3_ptr);
|
||||
*var_p_ptr = (void *) (&prs_ptr->app_ici_ptr);
|
||||
FOUT
|
||||
}
|
||||
*var_p_ptr = (void *)OPC_NIL;
|
||||
Binary file not shown.
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -81,6 +81,26 @@ TEST_OBJ = $(TEST_SRC:.cpp=.o)
|
|||
normTest: $(TEST_OBJ) libnorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(TEST_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
|
||||
|
||||
# (normThreadTest) test of threaded use of NORM API
|
||||
TTEST_SRC = $(COMMON)/normThreadTest.cpp
|
||||
TTEST_OBJ = $(TTEST_SRC:.cpp=.o)
|
||||
normThreadTest: $(TTEST_OBJ) libnorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(TTEST_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
|
||||
|
||||
# (normThreadTest2) alt test of threaded use of NORM API
|
||||
TTEST2_SRC = $(UNIX)/normThreadTest2.cpp
|
||||
TTEST2_OBJ = $(TTEST2_SRC:.cpp=.o)
|
||||
normThreadTest2: $(TTEST2_OBJ) libnorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(TTEST2_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
|
||||
|
||||
|
||||
# (npc) NORM Pre-Coder
|
||||
PCODE_SRC = $(COMMON)/normPrecode.cpp
|
||||
PCODE_OBJ = $(PCODE_SRC:.cpp=.o)
|
||||
npc: $(PCODE_OBJ) libnorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(PCODE_OBJ) $(LDFLAGS) libnorm.a $(LIBPROTO) $(LIBS)
|
||||
|
||||
|
||||
# (raft) command-line reliable tunnel helper
|
||||
RAFT_SRC = $(COMMON)/raft.cpp
|
||||
RAFT_OBJ = $(RAFT_SRC:.cpp=.o)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ SYSTEM_SRC =
|
|||
SYSTEM = linux
|
||||
|
||||
export CC = g++
|
||||
export SYSTEM_CFLAGS = -fPIC -Wall -pedantic -Wcast-align
|
||||
export SYSTEM_CFLAGS = -fPIC -Wall -Wcast-align
|
||||
export SYSTEM_SOFLAGS = -shared
|
||||
export RANLIB = ranlib
|
||||
export AR = ar
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ SYSTEM_SRC =
|
|||
SYSTEM = macosx
|
||||
|
||||
CC = g++
|
||||
SYSTEM_CFLAGS = -fPIC -Wall -pedantic -Wcast-align
|
||||
SYSTEM_CFLAGS = -fPIC -Wall -Wcast-align
|
||||
SYSTEM_SOFLAGS = -dynamiclib
|
||||
RANLIB = ranlib
|
||||
AR = ar
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NormDll", "NormDll.vcproj",
|
|||
{50D1D52B-702A-49FE-B490-9104FC72598D} = {50D1D52B-702A-49FE-B490-9104FC72598D}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npc", "npc.vcproj", "{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{50D1D52B-702A-49FE-B490-9104FC72598D} = {50D1D52B-702A-49FE-B490-9104FC72598D}
|
||||
{D7B0023C-8798-4918-8DA0-05C9054D70B9} = {D7B0023C-8798-4918-8DA0-05C9054D70B9}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfiguration) = preSolution
|
||||
Debug = Debug
|
||||
|
|
@ -50,6 +56,10 @@ Global
|
|||
{182006F3-188F-466E-89FE-8421C0478691}.Debug.Build.0 = Debug|Win32
|
||||
{182006F3-188F-466E-89FE-8421C0478691}.Release.ActiveCfg = Release|Win32
|
||||
{182006F3-188F-466E-89FE-8421C0478691}.Release.Build.0 = Release|Win32
|
||||
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Debug.ActiveCfg = Debug|Win32
|
||||
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Debug.Build.0 = Debug|Win32
|
||||
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Release.ActiveCfg = Release|Win32
|
||||
{32CF83E0-D2E0-4E43-8F7B-72BD0AB64647}.Release.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
EndGlobalSection
|
||||
|
|
|
|||
BIN
win32/Norm.suo
BIN
win32/Norm.suo
Binary file not shown.
|
|
@ -88,7 +88,7 @@
|
|||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\common;..\protolib\common;..\protolib\win32;NormDll3"
|
||||
PreprocessorDefinitions="NORM_USE_DLL, PROTO_DEBUG, WIN32, HAVE_ASSERT"
|
||||
PreprocessorDefinitions="NORM_USE_DLL;PROTO_DEBUG;WIN32;HAVE_ASSERT"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
BIN
wince/Norm.vco
BIN
wince/Norm.vco
Binary file not shown.
Loading…
Reference in New Issue