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