From 7a6c09ae1732bf4866513fefa7eb43bc58e3d3b4 Mon Sep 17 00:00:00 2001 From: bebopagogo Date: Sun, 26 Jan 2025 20:13:35 -0500 Subject: [PATCH] fixes to npc (NormPrecoder) and updated its documentation. Include 'auto' block sizing' feature --- doc/npcUserGuide.pdf | Bin 0 -> 44599 bytes doc/npcUserGuide.xml | 420 +++++++++++++++++++++++++++++++++++++ protolib | 2 +- src/common/normPrecode.cpp | 392 +++++++++++++++++++++------------- 4 files changed, 664 insertions(+), 150 deletions(-) create mode 100644 doc/npcUserGuide.pdf create mode 100644 doc/npcUserGuide.xml diff --git a/doc/npcUserGuide.pdf b/doc/npcUserGuide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9a0d79c1fc4a7b7bc8dd028b60f2ba1d73f1cae6 GIT binary patch literal 44599 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 literal 0 HcmV?d00001 diff --git a/doc/npcUserGuide.xml b/doc/npcUserGuide.xml new file mode 100644 index 0000000..1ad2527 --- /dev/null +++ b/doc/npcUserGuide.xml @@ -0,0 +1,420 @@ + + +
+ + <inlinemediaobject> + <imageobject> + <imagedata align="center" fileref="NormLogo.gif" scale="50"/> + </imageobject> + </inlinemediaobject> <command>npc</command> User Guide + + (NORM Precoder User Guide) + + npc User Guide + + + + Background + + The NACK-Oriented Reliable Multicast (NORM) protocol is capable of + supporting robust transmission of content to "silent" receivers that are + required or only capable of operating in an emission-controlled (EMCON) + manner. This capability is enabled when the NORM sender is configured to + proactively transmit Forward Error Correction (FEC) erasure coding content + as part of its original data transmission. For NACK-based operation, the + FEC repair packets are usually sent only reactively, in response to repair + requests (NACKs) from the receiver group. However, hybrid operation with a + combination of proactive FEC content and additional reactive FEC repairs + as needed is also supported. Similarly, a mix of nacking and silent + receivers may be supported with silent receivers capitalizing on the FEC + repair information sent proactively and/or reactively. The purpose of the + NORM Pre-Coder (npc) software utility described here is + to support additional robustness for purely-proactive sessions, where the + receivers are unable to request repair or retransmission of + content. + + The Naval Research Laboratory (NRL) reference implementation of the + NORM protocol includes support for 8-bit (and very soon 16-bit) + Reed-Solomon FEC encoding with additional support for other coding + algorithms (e.g., Low-Density Parity Check (LDPC)) planned for the future. + The NORM specification allows for different FEC algorithms to be applied + within the protocol. The current Reed-Solomon NORM FEC algorithms in the + NRL implementation are limited to modest code block sizes (With 16-bit + Reed-Solomon coding, larger block sizes will be allowed but very high data + rates may not be possible). For channels with random errors, the current + NORM FEC codecs are often adequate as there is flexibility in how the + encoded data can be partitioned into FEC blocks (a block consists of some + number of data segments (packets)) and the number of FEC parity packets + that can computed and possibly transmitted per source data block. For + channels with large bursts of packet loss (with respect to the configured + NORM FEC block size), it is quite possible that the number of lost packets + (erasures) that occur within a NORM FEC block may exceed the configured + erasure-filling capability. The npc utility was created + to "pre-encode" (and "post-decode") files for NORM transmission to silent + (non- NACKing) receivers by adding additional FEC encoding, and + importantly, interleaving of the FEC segments (packets) to re-distribute + bursts of packet losses as random losses over the entire file. It is thus + most applicable to very large files (with respect to FEC block + sizes). + + The NORM protocol is described in Internet Engineering Task Force + (IETF) Request For Comments (RFC) RFC 3940 and RFC 3941. These are + experimental RFC standards. These documents have been revised in recent + Internet-Drafts and it should be noted that the Naval Research Laboratory + (NRL) implementation of NORM that is represented here has been updated to + reflect the revised protocol. In addition to this demonstration + application, NRL provides a NORM protocol library with a well-defined API + that it is suitable for application development. Additionally, the NRL + source code distribution supports building the NORM protocol as a + component into ns-2 and OPNET network simulation + environments. Refer to the NRL NORM website <http://cs.itd.nrl.navy.mil/work/norm> + for these other components as well as up-to-date versions of this + demonstration application and documentation. + + The npc tool is designed to use in conjunction + with the NORM protocol and accompanying NORM file transfer examples that + are part of the NORM source code distribution. However, the encoded file + format that npc creates can be use with other + transports as well. The key is to align the segmentation parameters of the + npc configuration with that of the intended transport + mechanism. + + + + Overview + + The npc utility takes, as input", a file and + logically divides it into segments, adding cyclic-redundancy checksum + (CRC) to the segments, encoding the source segments with Reeed-Solomon + encoding (adding a configurable number of parity segments per FEC source + block), and interleaves the source and encoding segments to an output + file. The use of the CRC allows erasure to be detected and also provides + additional assurance of correct content delivery by possibly detecting bit + errors that may have been undetected during transport (i.e., link-layer + framing, Internet Protocol (IP), and/or User Datagram Protocol (UDP) + checksums). The interleaving by default is a block interleaver using the + entire file as a logical block, but a limit on the interleaving size can + be set to help increase the speed of the npc + encoding/decoding process. This may be useful for extremely large file + sizes. + + + + Usage + + The following is a synopsis of npc usage: + + npc {encode|decode} input <inFile> [output <outFile>][segment <segmentSize>] + {[[block <numData>][parity <numParity>]] | + [auto <parityPercentage>][bmax <maxBlockSize>]} + [imax <widthLimit>][ibuffer <bytes>][background][help][debug <debugLevel>] + + The npc utility may be instructed to either + "encode" a file (add FEC content and interleaving to the given + <inFile>) or "decode" a file that was previously encoded with + npc. The ".npc" file extension is suggested to + delineate files that are of the npc encoded format. + Note the "output" filename is optional. By default, npc + will use the filename of the <inFile> as the output filename, + replacing the '.' extension delimiter with a '_' (underscore) and adding + the ".npc" extension suffix. The npc format includes + some minimal "meta-data" in the first encoded <segmentSize> to + convey the file size and name of the original file. On decoding, if the + "output" file option is omitted, this "meta-data" is used to name the + decoded output file. + + The optional FEC parameters, + <segmentSize>, + <numData>, and + <numParity> control the logical segmentation, + blocking, and amount of FEC parity content added to the file. For use with + NORM, it is recommended that the + <segmentSize> value correspond to the same + segmentation size used for NORM transmission. The + <numData> (source segments per FEC encoding + block) and <numParity> parameters should be + selected to provide erasure filling coverage for the expected transmission + packet loss characteristics. Note that when used with proactive NORM FEC + transmission, the npc encoding provides an "inner" FEC + code and interleaving and the NORM protocol provides an "outer" FEC + encoding. The "outer" NORM code might be configured to deal with typical + random packet loss due to channel BER, etc and the "inner" + npc interleaving and coding could be correspondingly + configured to handle expected burst losses (e.g. outages) that might + occur. + + The <auto> option provides an + alternative means for setting the FEC encoding protection level instead of + using the <block> and + <parity> options. First the + <auto> option causes npc + to select a block size corresponding to the entire input file size (plus + the segment of meta data information that npc adds). Then, the + <auto> option + <parityPercentage> value is used to set the + number of parity packets per encoding block to the given percentage of the + automatically determined block size. For exanple, the command + " causes npc to set an + encoding rate of 100%. I.e., the parity segments sent will equal the + number of segments in a bock. Note that the + <parityPercentage> can even exceed 100% if + desired for high levels of loss protection. Also note that the percentage + here is _not_ a loss protection percentage with a 100% + <parityPercentage> value being able to + correct up to 50% errored or lost packets within a coding block. With 50% + uniform random packet loss, this would result in successful file transfer + about 50% of the time as, per Gaussian distribution, burst error + probabilities would result in half of blocks arriving with greater than + 50% packet loss and half with less than 50% packet loss. + + When the npc encoder uses the + "" command the npc decoder MUST + also use the "" command and be configured with the + same <segmentSize> and + <parityPercentage> values. Similarly when the + "" and "" commands are used + to explicitly set the <numdData> and + <numParity> at the encoder, the decoder MUST + be configured with the same corresponding options and values, again + including <segmentSize>. And for use with + NORM protocol transport, the <segmentSize> + parameter SHOULD be matched for best coding gain performance. The NORM + block size (numData) and parity (numParity) parameters may be set + indepedently. Basically, the NORM protocol proactive erasure coding can be + configured to deal with expected short term random packet loss while the + npc parameters can be configured to counter large burst + (or outage) losses. The inner/outer encoding approach that the combination + of npc and NORM provides, can allow for a sort of + multiplicative coding gain to deal well with both random packet loss and + bursts or outages with lower FEC overhead. However, when the + npc coding is configured (e.g., via the "auto" option) + to encapsulate an entire file into a single logical coding block, the + desired level of loss protection can be simply "dialed into" the + npc <parityPercentage> + option. The tradeoff is that the larger FEC block size increases the + computational requirement for file encoding and decoding. Future versions + of npc may provide additional FEC code types + + As basic example usage, to encode a file name "originalFile.txt" + with the default npc naming convention, FEC, and + interleaving parameters, use the following syntax: + + npc encode input originalFile.txt + + The default npc configuration is XXX. + + This will produce and output file named + "originalFile_txt.npc" in the current working + directory, The original file can be recovered (decoded) using the + syntax: + + npc decode input originalFile_txt.npc + + This will decode the ".npc" file, and in this case produce a file + named "originalFile.txt" in the current working + directory. (The file name information was stored in first "meta data" + segment of the ".npc" file). This default naming convention can be + overridden by using the npc "output" option. For example, the + syntax: + + npc decode input originalFile_txt.npc output file.txt + + will produce a file named "file.txt" that is + identical in content to "originalFile.txt". + + + Notes + + The FEC and interleaving parameters that are used for + npc encoding MUST be exactly matched to successfully + decode the encoded file. I.e., if the defaults are used for encoding, + the defaults must be used for decoding. The parameters that must match + include <segmentSize>, + <parityPercentage> and + <maxBlockSize> (or + <numData> and + <numParity>), and + <widthMax>. + + It is possible that in some cases it may be beneficial to apply + more proactive FEC content with the npc program + instead of with the NORM transport. The trade-offs are + scenario-specific. + + The NRL "norm" demonstration application has + commands included to support transport of npc encoded + files. The distinction here is that a file that _fails_ NORM transport + might still be successfully decoded with npc. There + are two receiver-side norm demo application options that apply + here: + + + + The "" command causes + norm to not delete (and attempt to postprocess) + "aborted" files (files that failed reliable NORM transport). + + + + The norm "" + command should be applied for silent-receivers to more immediately + deliver "failed" files to the application for post-processing (i.e., + attempted npc decoding) + + + + + + + <command>npc</command> Command Reference + + The following table describes each of the npc + commands available in the command-line syntax. + + + + + + + + + + | + + + Determine whether npc is to encode + or decode the given <inFile>. This + option is required and only one should be given. + + + + <inFile> + + Specifies the file to be processed. Required + command. + + + + <outFile> + + Specifies the name of the output file to be produced. + Overrides the default npc output file naming + convention. Optional. + + + + <segmentSize> + + Sets the segmentation size (e.g., packet payload + size) in bytes. Note four bytes of the + <segmentSize> are used for a 32-bit + CRC that npc applies to each segment. (Default + <segmentSize> is 1024 + bytes) + + + + <numData> + + Specify the number of source data segments (packets) + per npc FEC coding block. (Default block sizing is + auto) + + + + <numParity> + + Specify the number of FEC parity segments (packets) + added per npc FEC coding block. (Default is 2 + segments). + + + + <parityPercentage> + + Specifies automatic FEC block sizing with + <parityPercentage> indicating the + percentage of FEC parity segments to include per block. The "auto" + block sizing sets the block size as large as possible to treat the + entire files as one logical FEC block to maximize FEC performance. + The maximum possible block size currently supported by npc are + blocks where (numData + numParity) is less than or equal to 65536. + The maximum buffer size can be limited by using the + command. + + + + <maxBlockSize> + + Limits the maximum block size when the + command is used for automatic block sizing + (Default is 65536) + + + + <widthMax> + + Limits interleaving of encoded file to a maximum + interleaver width of <widthMax> + segments. A value of ZERO (or less) defaults to + npc calculating a block interleaver that + encompasses the entire encoded file size. For extremely large + files, this option may be beneficial to limit file seeking + operations required to interleave the file. If the encoded file + size is less than + <widthMax>*<widthMax> + segments, npc will again calculate its own + maximum block size. (Default is 1000 segments interleaver depth + (i.e., about 1 Gbyte interleaver size with the default 1024 byte + <segmentSize> value)) + + + + <bufferSize> + + This sets the maximum memory (in bytes) that + npc allocates for encoding. A larger value + allows npc to perform file input/output with + less seeking and improved encoding/decoding times can be achieved. + (Default is 1.5 GByte) + + + + <debugLevel> + + Specifies debug output verbosity. Higher number is + more verbose debugging information. (Default is + ZERO). + + + + + + Sets percentage of received messages that are + randomly dropped (for testing purposes). Default = 0.0 + percent. + + + + + + Displays npc usage + statement. + + + + + +
diff --git a/protolib b/protolib index a286069..e5ac157 160000 --- a/protolib +++ b/protolib @@ -1 +1 @@ -Subproject commit a28606945f6b507760548b242b6706b51b5b69d3 +Subproject commit e5ac157eb61e991c74acfe39e2febe8c09e187dc diff --git a/src/common/normPrecode.cpp b/src/common/normPrecode.cpp index 10c5ddc..75bc83c 100755 --- a/src/common/normPrecode.cpp +++ b/src/common/normPrecode.cpp @@ -1,6 +1,6 @@ #include "protoApp.h" -#include "normFile.h" +#include "protoFile.h" // Commment this #define out to use new, faster RS8 codec instead // (TBD - provide option use 16-bit Reed Solomon for large block sizes?) @@ -10,6 +10,7 @@ #include "normEncoderMDP.h" #else #include "normEncoderRS8.h" +#include "normEncoderRS16.h" #endif // if/else USE_MDP_FEC #include // for BYTE_ORDER macro @@ -38,22 +39,22 @@ class NormPrecodeApp : public ProtoApp bool Encode(); bool Decode(); - void InitInterleaver(NormFile::Offset numSegments); - NormFile::Offset ComputeInterleaverOffset(NormFile::Offset segmentId, NormFile::Offset numSegments); - NormFile::Offset ComputeSegmentOffset(NormFile::Offset interleaverId, NormFile::Offset numSegments); + void InitInterleaver(ProtoFile::Offset numSegments); + ProtoFile::Offset ComputeInterleaverOffset(ProtoFile::Offset segmentId, ProtoFile::Offset numSegments); + ProtoFile::Offset ComputeSegmentOffset(ProtoFile::Offset interleaverId, ProtoFile::Offset numSegments); // CRC32 checksum stuff static const UINT32 CRC32_TABLE[256]; static UINT32 ComputeCRC32(const char* buffer, unsigned int buflen); - static const NormFile::Offset SEGMENT_MIN; - static const NormFile::Offset SEGMENT_MAX; + static const ProtoFile::Offset SEGMENT_MIN; + static const ProtoFile::Offset SEGMENT_MAX; // We use these assuming IEEE754 floating point - static NormFile::Offset ntoho(NormFile::Offset offset) + static ProtoFile::Offset ntoho(ProtoFile::Offset offset) { # if BYTE_ORDER == LITTLE_ENDIAN - NormFile::Offset result; - switch (sizeof(NormFile::Offset)) + ProtoFile::Offset result; + switch (sizeof(ProtoFile::Offset)) { case 8: { @@ -78,37 +79,40 @@ class NormPrecodeApp : public ProtoApp return offset; #endif // if/else __BIG_ENDIAN } - static NormFile::Offset htono(NormFile::Offset offset) + static ProtoFile::Offset htono(ProtoFile::Offset offset) { return ntoho(offset); } - NormFile in_file; - char in_file_path[PATH_MAX]; - NormFile out_file; - bool encode; + ProtoFile in_file; + char in_file_path[PATH_MAX]; + ProtoFile out_file; + bool encode; - unsigned int segment_size; // should be same as NORM segment size - unsigned int num_data; - unsigned int num_parity; + unsigned int segment_size; // should be same as NORM segment size + unsigned int num_data; + unsigned int num_parity; + double parity_fraction; // invokes "auto" block/parity sizing based on file size + ProtoFile::Offset b_max; // optional max block size for "auto" mode - NormFile::Offset i_max; // max interleaver dimension - NormFile::Offset i_buffer_max; // Read buffer max (bigger yields less seeking) + ProtoFile::Offset i_max; // max interleaver dimension + ProtoFile::Offset i_buffer_max; // Read buffer max (bigger yields less seeking) - NormFile::Offset interleaver_width; - NormFile::Offset interleaver_height; - NormFile::Offset interleaver_size; // (width * height) + ProtoFile::Offset interleaver_width; + ProtoFile::Offset interleaver_height; + ProtoFile::Offset interleaver_size; // (width * height) }; // end class NormPrecodeApp // Our application instance PROTO_INSTANTIATE_APP(NormPrecodeApp) -const NormFile::Offset NormPrecodeApp::SEGMENT_MIN = 8; -const NormFile::Offset NormPrecodeApp::SEGMENT_MAX = 8192; +const ProtoFile::Offset NormPrecodeApp::SEGMENT_MIN = 8; +const ProtoFile::Offset NormPrecodeApp::SEGMENT_MAX = 8192; NormPrecodeApp::NormPrecodeApp() : encode(true), segment_size(1024), num_data(196), num_parity(4), + parity_fraction(-1.0), b_max(65536), i_max(1000), i_buffer_max(1500000000) { in_file_path[0] = '\0'; @@ -122,6 +126,7 @@ void NormPrecodeApp::Usage() { fprintf(stderr, "Usage: npc {encode|decode} input [output ]\n" " [segment ][block numData][parity numParity]\n" + " [auto ]\n" " [background][help][debug \n"); } // end NormPrecodeApp::Usage() @@ -136,6 +141,8 @@ const char* const NormPrecodeApp::cmd_list[] = "+segment", // set segment size (default = 1024) "+block", // set block size (default = 128) "+parity", // set parity per block (default = 2) + "+auto", // set auto block/parity mode (specifies parity_fraction as percent) + "+bmax", // limit maximum allowed block size for "auto" operation (default = 65536) "+imax", // set interleaver max dimension "+ibuffer", // set imax interleaver buffer (buffer is used if interleaver size fits) "-background", // run w/out command shel (Win32) @@ -208,24 +215,45 @@ bool NormPrecodeApp::OnCommand(const char* cmd, const char* val) } else if (!strncmp("block", cmd, len)) { - int numData = atoi(val); - if ((numData < 1) || (numData > 127)) + unsigned short numData; + if (1 != sscanf(val, "%hu", &numData)) { - PLOG(PL_FATAL, "npc: error: block out of range\n"); + PLOG(PL_FATAL, "npc: error: invalid block value!\n"); return false; } num_data = numData; } else if (!strncmp("parity", cmd, len)) { - int numParity = atoi(val); - if ((numParity < 0) || (numParity > 127)) + unsigned short numParity; + if (1 != sscanf(val, "%hu", &numParity)) { - PLOG(PL_FATAL, "npc: error: parity out of range\n"); + PLOG(PL_FATAL, "npc: error: invalid block value!\n"); return false; } num_parity = numParity; } + else if (!strncmp("auto", cmd, len)) + { + double percent; + if (1 != sscanf(val,"%lf", &percent)) + { + PLOG(PL_FATAL, "npc: error: invalid block value!\n"); + return false; + } + if (percent < 0.0) + { + PLOG(PL_FATAL, "npc: error: invalid block value!\n"); + return false; + } + parity_fraction = percent/100.0; + } + else if (!strncmp("bmax", cmd, len)) + { + int bMax = atoi(val); + if (bMax <= 0) bMax = 65536; + b_max = bMax; + } else if (!strncmp("imax", cmd, len)) { int iMax = atoi(val); @@ -318,6 +346,49 @@ bool NormPrecodeApp::OnStartup(int argc, const char*const* argv) return false; } + if (parity_fraction >= 0.0) + { + // Set up auto mode using input file size + // (This uses the max block size possible for the file given "segment_size") + ProtoFile::Offset fileSize = in_file.GetSize(); + unsigned int blockSize; + if (encode) + { + // (segment_size - 4) here accounts for 4-byte CRC + blockSize = (unsigned int) (fileSize / (segment_size - 4)); + if (0 != (fileSize % (segment_size - 4))) + blockSize += 1; + blockSize += 1; // for meta data segment + } + else + { + // Need to use post decode size + ProtoFile::Offset numSegments = fileSize / segment_size; + ASSERT(0 == (fileSize % segment_size)); + blockSize = (unsigned int)((numSegments / (1.0 + parity_fraction)) + 0.5); + } + if (blockSize > b_max) blockSize = b_max; + unsigned int numParity = (unsigned int)((parity_fraction * blockSize) + 0.5); + if ((blockSize + numParity) > 65536) + { + // need to scale down to fit within max possible FEC block size + double scaleFactor = 65536.0 / ((double)(blockSize + numParity)); + blockSize = (unsigned int)(scaleFactor * blockSize); + numParity = (unsigned int)(scaleFactor * numParity); + } + //TRACE("auto fileSize:%llu block:%u parity:%u\n", fileSize, blockSize, numParity); + num_data = blockSize; + num_parity = numParity; + } + + // Validate num_data / num_parity params + unsigned int totalBlockSize = num_data + num_parity; + if (totalBlockSize > 65536) + { + PLOG(PL_FATAL, "npc: error: numData/numParity total exceeds max block size!\n"); + return false; + } + if (encode) return Encode(); else @@ -335,9 +406,9 @@ void NormPrecodeApp::OnShutdown() #define DIFF_T(a,b) (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) ) -void NormPrecodeApp::InitInterleaver(NormFile::Offset numSegments) +void NormPrecodeApp::InitInterleaver(ProtoFile::Offset numSegments) { - interleaver_width = (NormFile::Offset)(sqrt((double)numSegments)); + interleaver_width = (ProtoFile::Offset)(sqrt((double)numSegments)); interleaver_height = numSegments / interleaver_width; if (0 != (numSegments % interleaver_height)) interleaver_height++; // Limit dimension if "i_max" is set to non-zero value @@ -345,19 +416,19 @@ void NormPrecodeApp::InitInterleaver(NormFile::Offset numSegments) interleaver_height = interleaver_width = i_max; interleaver_size = interleaver_height * interleaver_width; PLOG(PL_INFO, "npc interleaver width:%lu height:%lu segments (numSeg:%lld)\n", - (unsigned long)interleaver_width, (unsigned long)interleaver_height, numSegments); + (unsigned long)interleaver_width, (unsigned long)interleaver_height, numSegments); } // end NormPrecodeApp::InitInterleaver() -NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segmentId, NormFile::Offset numSegments) +ProtoFile::Offset NormPrecodeApp::ComputeInterleaverOffset(ProtoFile::Offset segmentId, ProtoFile::Offset numSegments) { ASSERT(0 != interleaver_height); - NormFile::Offset interleaverWidth = interleaver_width; - NormFile::Offset interleaverHeight = interleaver_height; - NormFile::Offset interleaverSize = interleaver_size; - NormFile::Offset blockId; + ProtoFile::Offset interleaverWidth = interleaver_width; + ProtoFile::Offset interleaverHeight = interleaver_height; + ProtoFile::Offset interleaverSize = interleaver_size; + ProtoFile::Offset blockId; if (i_max > 0) { blockId = segmentId / interleaverSize; @@ -366,26 +437,25 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme else { blockId = 0; - } - + } // Check to see if we're in the last block - NormFile::Offset lastSegmentId = numSegments - 1; - NormFile::Offset lastBlockId = lastSegmentId / interleaverSize; + ProtoFile::Offset lastSegmentId = numSegments - 1; + ProtoFile::Offset lastBlockId = lastSegmentId / interleaverSize; if ((blockId == lastBlockId) && (0 != (numSegments % interleaverSize))) { // This block is smaller than our usual interleaver_size, // so we're going to "square things up" to maximize the // distance of this last block within interleaver_size constraint - NormFile::Offset lastBlockSize = (numSegments % interleaverSize); - interleaverWidth = (NormFile::Offset)(sqrt((double)lastBlockSize)); + ProtoFile::Offset lastBlockSize = (numSegments % interleaverSize); + interleaverWidth = (ProtoFile::Offset)(sqrt((double)lastBlockSize)); interleaverHeight = lastBlockSize / interleaverWidth; if (0 != (lastBlockSize % interleaverHeight)) interleaverHeight++; } - NormFile::Offset interleaverCol = segmentId / interleaverHeight; - NormFile::Offset interleaverRow = segmentId % interleaverHeight; - NormFile::Offset interleaverId = ((interleaverRow * interleaverWidth) + interleaverCol); + ProtoFile::Offset interleaverCol = segmentId / interleaverHeight; + ProtoFile::Offset interleaverRow = segmentId % interleaverHeight; + ProtoFile::Offset interleaverId = ((interleaverRow * interleaverWidth) + interleaverCol); if (0 != blockId) interleaverId += (blockId * interleaver_size); @@ -394,18 +464,18 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme if (interleaverId >= numSegments) { // We're here because we hit a "hole" in the rectangle - NormFile::Offset lastSegmentId = numSegments - 1; + ProtoFile::Offset lastSegmentId = numSegments - 1; if (0 != blockId) { interleaverId = interleaverId % interleaverSize; lastSegmentId = lastSegmentId % interleaverSize; } // Find non-interleaved position of lastSegmentId within interleaver - NormFile::Offset maxRow = lastSegmentId / interleaverWidth; - NormFile::Offset maxCol = lastSegmentId % interleaverWidth; + ProtoFile::Offset maxRow = lastSegmentId / interleaverWidth; + ProtoFile::Offset maxCol = lastSegmentId % interleaverWidth; // There may be empty rows if lastSegmentId small wr2 interleaver size - NormFile::Offset emptyRows = interleaverHeight - maxRow - 1; - NormFile::Offset delta = 1 + emptyRows * interleaverCol; + ProtoFile::Offset emptyRows = interleaverHeight - maxRow - 1; + ProtoFile::Offset delta = 1 + emptyRows * interleaverCol; if (interleaverCol > maxCol) { delta += interleaverRow - maxRow; @@ -417,8 +487,8 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme } // Find interleaved position of lastSegmentId within interleaver - NormFile::Offset lastCol = lastSegmentId / interleaverHeight; - NormFile::Offset lastRow = lastSegmentId % interleaverHeight; + ProtoFile::Offset lastCol = lastSegmentId / interleaverHeight; + ProtoFile::Offset lastRow = lastSegmentId % interleaverHeight; // Remap this segment to the "delta" interleaved position after "lastSegmentId" lastRow += delta; @@ -445,12 +515,12 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme } // end NormPrecodeApp::ComputeInterleaverOffset() -NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleaverId, NormFile::Offset numSegments) +ProtoFile::Offset NormPrecodeApp::ComputeSegmentOffset(ProtoFile::Offset interleaverId, ProtoFile::Offset numSegments) { - NormFile::Offset interleaverWidth = interleaver_width; - NormFile::Offset interleaverHeight = interleaver_height; - NormFile::Offset interleaverSize = interleaver_size; - NormFile::Offset blockId; + ProtoFile::Offset interleaverWidth = interleaver_width; + ProtoFile::Offset interleaverHeight = interleaver_height; + ProtoFile::Offset interleaverSize = interleaver_size; + ProtoFile::Offset blockId; if (i_max > 0) { blockId = interleaverId / interleaverSize; @@ -462,10 +532,10 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav } - NormFile::Offset interleaverRow = interleaverId / interleaverWidth; - NormFile::Offset interleaverCol = interleaverId % interleaverWidth; + ProtoFile::Offset interleaverRow = interleaverId / interleaverWidth; + ProtoFile::Offset interleaverCol = interleaverId % interleaverWidth; - NormFile::Offset segmentId = interleaverCol * interleaverHeight + interleaverRow; + ProtoFile::Offset segmentId = interleaverCol * interleaverHeight + interleaverRow; if (0 != blockId) segmentId += (blockId * interleaver_size); @@ -473,26 +543,26 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav if (segmentId >= numSegments) { // It was a "hole", so find its hole delta - NormFile::Offset lastSegmentId = numSegments - 1; + ProtoFile::Offset lastSegmentId = numSegments - 1; if (0 != blockId) { segmentId = segmentId % interleaverSize; lastSegmentId = lastSegmentId % interleaverSize; } // Here maxRow/maxCol are wr2 _interleaved_ position of lastSegmentId - NormFile::Offset maxCol = lastSegmentId / interleaverHeight; - NormFile::Offset maxRow = lastSegmentId % interleaverHeight; + ProtoFile::Offset maxCol = lastSegmentId / interleaverHeight; + ProtoFile::Offset maxRow = lastSegmentId % interleaverHeight; // AS above, this assertion _should_ be true if we're "square" enough // (it does break when interleaver width is much greater than height, // so, some day (TBD) we may want to generalize this remapping trick more??? ASSERT(interleaverCol >= maxCol); - NormFile::Offset delta = (interleaverCol - maxCol)*(maxRow+1) + interleaverRow - maxRow; + ProtoFile::Offset delta = (interleaverCol - maxCol)*(maxRow+1) + interleaverRow - maxRow; // Then, remap "delta" to find _original_ "hole" position (corrected interleaver position) // Here maxRow/maxCol are wr2 _source_ position of lastSegmentId maxRow = lastSegmentId / interleaverWidth; maxCol = lastSegmentId % interleaverWidth; - NormFile::Offset emptyRows = interleaverHeight - maxRow - 1; + ProtoFile::Offset emptyRows = interleaverHeight - maxRow - 1; if (delta <= (emptyRows*(maxRow + 1))) { // in first area @@ -510,7 +580,7 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav if (0 != blockId) segmentId += (blockId * interleaver_size); ASSERT(segmentId < numSegments); } - NormFile::Offset segmentOffset = segment_size * segmentId; + ProtoFile::Offset segmentOffset = segment_size * segmentId; return segmentOffset; } // end NormPrecodeApp::ComputeSegmentOffset() @@ -544,35 +614,43 @@ bool NormPrecodeApp::Encode() struct timeval t1, t2; ProtoSystemTime(t1); - NormFile::Offset fileSize = in_file.GetSize(); + ProtoFile::Offset fileSize = in_file.GetSize(); // We reserve 4 bytes for our CRC (used to detect erasures) unsigned int dataSegmentSize = segment_size - 4; - NormFile::Offset numInputSegments = 1 + fileSize / dataSegmentSize; + ProtoFile::Offset numInputSegments = 1 + fileSize / dataSegmentSize; unsigned int lastFecSegSize = (unsigned int)(fileSize % dataSegmentSize); - if (0 != lastFecSegSize) numInputSegments++; + if (0 != lastFecSegSize) + numInputSegments++; + else + lastFecSegSize = dataSegmentSize; // Calculate FEC block size(s) - NormFile::Offset numBlocks = numInputSegments / num_data; + ProtoFile::Offset numBlocks = numInputSegments / num_data; unsigned int fecBlockSize = num_data; unsigned int lastBlockSize = (unsigned int)(numInputSegments % num_data); - if (0 != lastBlockSize) numBlocks++; - NormFile::Offset lastBlockId = numBlocks - 1; + if (0 != lastBlockSize) + numBlocks++; + else + lastBlockSize = num_data; + ProtoFile::Offset lastBlockId = numBlocks - 1; // 0) Calculate "out_file" size and determine interleaver width and height - NormFile::Offset numOutputSegments = + ProtoFile::Offset numOutputSegments = ((numBlocks - 1) * (fecBlockSize + num_parity)) + lastBlockSize + num_parity; InitInterleaver(numOutputSegments); // 1) Init our FEC encoder -#ifdef USE_MDP_FEC - NormEncoderMDP encoder; -#else - NormEncoderRS8 encoder; -#endif // if/else USE_MDP_FEC + NormEncoder* encoderPtr; + if ((num_data + num_parity) > 256) + encoderPtr = new NormEncoderRS16; + else + encoderPtr = new NormEncoderRS8; + NormEncoder& encoder = *encoderPtr; + if (!encoder.Init(num_data, num_parity, dataSegmentSize)) // 4 CRC bytes are _not_ encoded { PLOG(PL_FATAL, "npc: error initializing FEC encoder\n"); @@ -581,7 +659,7 @@ bool NormPrecodeApp::Encode() // Determine number of segments to allocate for FEC encoding and // interleaver buffering if applicable - NormFile::Offset interleaverBytes = interleaver_size * segment_size; + ProtoFile::Offset interleaverBytes = interleaver_size * segment_size; char* iBuffer = NULL; bool useBuffering = false; if (interleaverBytes <= i_buffer_max) @@ -625,20 +703,20 @@ bool NormPrecodeApp::Encode() // (TBD) This could be built directly into iBuffer segment zero char metaData[SEGMENT_MAX+4]; memset(metaData, 0, SEGMENT_MAX); - NormFile::Offset sz = fileSize; - if (sizeof(NormFile::Offset) == 8) + ProtoFile::Offset sz = fileSize; + if (sizeof(ProtoFile::Offset) == 8) { sz = htono(fileSize); memcpy(metaData, &sz, 8); } - else if (sizeof(NormFile::Offset) == 4) + else if (sizeof(ProtoFile::Offset) == 4) { sz = htonl((UINT32)sz); memcpy(metaData + 4, &sz, 4); } else { - PLOG(PL_FATAL, "npc: error: unsupported file offset size (%d bytes)\n", sizeof(NormFile::Offset)); + PLOG(PL_FATAL, "npc: error: unsupported file offset size (%d bytes)\n", sizeof(ProtoFile::Offset)); return false; } // put in_file_path file name portion into middle section of "metaData" @@ -653,24 +731,23 @@ bool NormPrecodeApp::Encode() // 2) Read "in_file" segments, encode, and output to "out_file" PLOG(PL_ALWAYS, "npc: encoding file ... (progress: 0%%)"); // State to track/display encoding progress - NormFile::Offset progressThreshold = numOutputSegments / 100; - double progressIncrement = 100.0; - if (progressThreshold > 1) - progressIncrement = (double)numOutputSegments / (double)progressThreshold; - else - progressThreshold = numOutputSegments; // small number of segments - NormFile::Offset progressCounter = 0; + ProtoFile::Offset progressThreshold = numInputSegments / 100; + if (progressThreshold < 1) progressThreshold = 1; + ProtoFile::Offset progressCounter = 0; int progressPercent = 0; - NormFile::Offset blockId = 0; + ProtoFile::Offset blockId = 0; unsigned int parityCount = 0; bool parityReady = false; - NormFile::Offset inputSegmentId = 0; - NormFile::Offset outputSegmentId = 0; + ProtoFile::Offset inputSegmentId = 0; + ProtoFile::Offset outputSegmentId = 0; while (outputSegmentId < numOutputSegments) { - NormFile::Offset interleaverOffset = ComputeInterleaverOffset(outputSegmentId, numOutputSegments); + // The ComputerInterleaverOffset() call here retrieves the _output_ offset location that the + // current input segment will be mapped to. I.e., data is read from the input order, but mapped + // to interleaved output position via this offset + ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(outputSegmentId, numOutputSegments); char* segment = useBuffering ? (iBuffer + (interleaverOffset % interleaverBytes)) : iBuffer; if (parityReady) { @@ -696,25 +773,30 @@ bool NormPrecodeApp::Encode() else { // B) Read in data portion of next "segment" - unsigned int bytesToRead; + unsigned int expectedBytes; if (inputSegmentId != numInputSegments) { - bytesToRead = dataSegmentSize; + expectedBytes = dataSegmentSize; } else { memset(segment, 0, dataSegmentSize); - bytesToRead = lastFecSegSize; + expectedBytes = lastFecSegSize; } - if (in_file.Read(segment, bytesToRead) != bytesToRead) + unsigned int bytesToRead = expectedBytes; + if (!in_file.Read(segment, bytesToRead)) { PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString()); return false; } + if (bytesToRead != expectedBytes) + { + PLOG(PL_FATAL, "\nnpc: unexpected end-of-file: %s\n", GetErrorString()); + return false; + } + if (0 != bytesToRead) segment[bytesToRead] = '\0'; } // C) Encode and check for parity readiness - //TRACE("outputSegmentId:%lu\n", outputSegmentId); - encoder.Encode(outputSegmentId % fecBlockSize, segment, parityVec); unsigned int numData = (blockId != lastBlockId) ? fecBlockSize : lastBlockSize; if (numData == ++parityCount) @@ -722,20 +804,29 @@ bool NormPrecodeApp::Encode() parityCount = num_parity; parityReady = true; } + if (++progressCounter >= progressThreshold) + { + progressPercent = int((double)inputSegmentId / (double)numInputSegments); + if (progressPercent < 9) + PLOG(PL_ALWAYS, "\b\b\b%d%%)", progressPercent + 1); + else if (progressPercent < 99) + PLOG(PL_ALWAYS, "\b\b\b\b%d%%)", progressPercent + 1); + if (progressPercent < 99) progressPercent++; + progressCounter = 0; + } } // E) Calculate and add CRC32 checksum to each "segment" UINT32 checksum = ComputeCRC32(segment, dataSegmentSize); checksum = htonl(checksum); - memcpy(segment+dataSegmentSize, &checksum, 4); + memcpy(segment+dataSegmentSize, &checksum, 4); if (useBuffering) { - outputSegmentId++; if ((0 == (outputSegmentId % interleaver_size)) || (outputSegmentId == numOutputSegments)) { // Output our buffered interleaver block from memory (iBuffer) to "out_file" - NormFile::Offset bytesToWrite; + ProtoFile::Offset bytesToWrite; if ((outputSegmentId != numOutputSegments) || (numOutputSegments == interleaver_size)) bytesToWrite = interleaver_size; else @@ -764,23 +855,12 @@ bool NormPrecodeApp::Encode() return false; } outputSegmentId++; - - } - if (++progressCounter >= progressThreshold) - { - if (progressPercent < 9) - PLOG(PL_ALWAYS, "\b\b\b%d%%)", progressPercent + 1); - else if (progressPercent < 99) - PLOG(PL_ALWAYS, "\b\b\b\b%d%%)", progressPercent + 1); - if (progressPercent < 99) progressPercent++; - progressCounter = 0; - } + } } if (progressPercent < 10) PLOG(PL_ALWAYS, "\b\b\b100%%)\n"); else PLOG(PL_ALWAYS, "\b\b\b\b100%%)\n"); - in_file.Close(); out_file.Close(); @@ -801,15 +881,15 @@ bool NormPrecodeApp::Encode() bool NormPrecodeApp::Decode() { // 1) Determine file size and init interleaving - NormFile::Offset inputFileSize = in_file.GetSize(); - NormFile::Offset numInputSegments = inputFileSize / segment_size; + ProtoFile::Offset inputFileSize = in_file.GetSize(); + ProtoFile::Offset numInputSegments = inputFileSize / segment_size; if (0 != (inputFileSize % segment_size)) { PLOG(PL_FATAL, "npc: error: input file size not integral number of given \n"); return false; } // Reverse calculate the FEC blocking - NormFile::Offset numFecBlocks = numInputSegments / (num_data + num_parity); + ProtoFile::Offset numFecBlocks = numInputSegments / (num_data + num_parity); unsigned int fecBlockSize = num_data; unsigned int lastFecBlockSize = (unsigned int)(numInputSegments % (num_data + num_parity)); if (0 != lastFecBlockSize) @@ -818,17 +898,23 @@ bool NormPrecodeApp::Decode() lastFecBlockSize -= num_parity; numFecBlocks++; } - NormFile::Offset lastFecBlockId = numFecBlocks - 1; + else + { + lastFecBlockSize = num_data; + } + ProtoFile::Offset lastFecBlockId = numFecBlocks - 1; // Calculate interleaver dimensions from file size // set "interleaver_size", etc InitInterleaver(numInputSegments); // 2) init FEC decoder -#ifdef USE_MDP_FEC - NormDecoderMDP decoder; -#else - NormDecoderRS8 decoder; -#endif // if/else USE_MDP_FEC + NormDecoder* decoderPtr; + if ((num_data + num_parity) > 256) + decoderPtr = new NormDecoderRS16; + else + decoderPtr = new NormDecoderRS8; + NormDecoder& decoder = *decoderPtr; + unsigned int dataSegmentSize = segment_size - 4; // leaves space for our CRC if (!decoder.Init(num_data, num_parity, dataSegmentSize)) { @@ -842,7 +928,7 @@ bool NormPrecodeApp::Decode() // interleaver buffering if applicable char* iBuffer = NULL; bool useBuffering = false; - NormFile::Offset interleaverBytes = interleaver_size * segment_size; + ProtoFile::Offset interleaverBytes = interleaver_size * segment_size; if ((interleaverBytes <= i_buffer_max))// && //((num_data + num_parity) <= interleaver_size) && @@ -893,32 +979,29 @@ bool NormPrecodeApp::Decode() } - PLOG(PL_FATAL, "npc: decoding file ... (progress: 0%%)"); + PLOG(PL_ALWAYS, "npc: decoding file ... (progress: 0%%)"); // State to track/display decoding progress - NormFile::Offset progressThreshold = numInputSegments / 100; - double progressIncrement = 100.0; - if (progressThreshold > 1) - progressIncrement = (double)numInputSegments / (double)progressThreshold; - else - progressThreshold = numInputSegments; // small number of segments - NormFile::Offset progressCounter = 0; + ProtoFile::Offset progressThreshold = numInputSegments / 100; + if (progressThreshold <= 1) + progressThreshold = numInputSegments; // small number of segments + ProtoFile::Offset progressCounter = 0; int progressPercent = 0; // Read and decode each block in "in_file" - NormFile::Offset fecBlockId = 0; - NormFile::Offset outFileSize = 0; + ProtoFile::Offset fecBlockId = 0; + ProtoFile::Offset outFileSize = 0; - NormFile::Offset lastInterleaverBlockId = numInputSegments / interleaver_size; - NormFile::Offset lastInterleaverBytes = (numInputSegments % interleaver_size) * segment_size; - NormFile::Offset interleaverBlockId = 0; + ProtoFile::Offset lastInterleaverBlockId = numInputSegments / interleaver_size; + ProtoFile::Offset lastInterleaverBytes = (numInputSegments % interleaver_size) * segment_size; + ProtoFile::Offset interleaverBlockId = 0; unsigned int erasureCount = 0; unsigned int segmentCount = 0; enum State {READING, ADVANCING, DECODING}; State state = READING; - NormFile::Offset inputSegmentId = 0; + ProtoFile::Offset inputSegmentId = 0; while ((inputSegmentId < numInputSegments) || (DECODING == state)) { switch (state) @@ -927,15 +1010,21 @@ bool NormPrecodeApp::Decode() if (useBuffering) { // Read in a full interleaver block - NormFile::Offset bytesToRead; + size_t bytesToRead; if (interleaverBlockId != lastInterleaverBlockId) bytesToRead = interleaverBytes; else bytesToRead = lastInterleaverBytes; - if (in_file.Read(iBuffer, bytesToRead) != bytesToRead) + + if (!in_file.Read(iBuffer, bytesToRead)) { PLOG(PL_FATAL, "\nnpc: error reading input file: %s\n", GetErrorString()); return false; + } + if (0 == bytesToRead) + { + PLOG(PL_FATAL, "\nnpc: error reading input file: unexpected end-of-file\n"); + return false; } interleaverBlockId++; state = ADVANCING; @@ -947,7 +1036,7 @@ bool NormPrecodeApp::Decode() for (unsigned int i = 0 ; i < (numData + num_parity); i++) { // Calc offset (de-interleave) and seek - NormFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments); + ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments); // seek to interleaver offset if (!in_file.Seek(interleaverOffset)) { @@ -955,11 +1044,17 @@ bool NormPrecodeApp::Decode() return false; } // Read segment - if (in_file.Read(fecVec[i], segment_size) != segment_size) + unsigned int bytesToRead = segment_size; + if (!in_file.Read(fecVec[i], bytesToRead)) { PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString()); return false; } + if (bytesToRead != segment_size) + { + PLOG(PL_FATAL, "\nnpc: read() error: incomplete segment (len: %lu out of %lu bytes)\n", bytesToRead, segment_size); + return false; + } inputSegmentId++; // Validate checksum (detects errors/ erasures) @@ -988,7 +1083,7 @@ bool NormPrecodeApp::Decode() unsigned int priorSegmentCount = segmentCount; for (; segmentCount < (numData + num_parity); segmentCount++) { - NormFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments); + ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments); fecVec[segmentCount] = iBuffer + (interleaverOffset % interleaverBytes); inputSegmentId++; // Validate checksum (detects errors/ erasures) @@ -1045,7 +1140,7 @@ bool NormPrecodeApp::Decode() if((0 == fecBlockId) && (0 == i)) { // First segment of first block is our "meta_data" with file size info - switch (sizeof(NormFile::Offset)) + switch (sizeof(ProtoFile::Offset)) { case 8: memcpy(&outFileSize, fecVec[0], 8); @@ -1078,6 +1173,7 @@ bool NormPrecodeApp::Decode() { // Last segment, so calculate "lastSegmentSize" segmentSize = (unsigned int)(outFileSize % segmentSize); + if (0 == segmentSize) segmentSize = segment_size - 4; } if (out_file.Write(fecVec[i], segmentSize) != segmentSize) { @@ -1120,8 +1216,6 @@ bool NormPrecodeApp::Decode() } // end NormPrecodeApp::Decode() - - /*****************************************************************/ /* */ /* CRC LOOKUP TABLE */