Switch fountain encoding to use 2-byte blocks, because...

...this is the current wirehair limitation anyway.
Also added some test cases!
This commit is contained in:
sz 2020-06-17 18:50:26 -05:00
parent 75e88a6d72
commit e4b02d4a85
5 changed files with 103 additions and 14 deletions

View File

@ -10,7 +10,7 @@ template <unsigned _bufferSize> // 599
class fountain_decoder_stream
{
public:
static const unsigned _headerSize = 3;
static const unsigned _headerSize = 2;
public:
fountain_decoder_stream(unsigned dataSize)
@ -42,7 +42,7 @@ public:
{
// if we're full
_buffIndex = 0;
unsigned blockId = ((unsigned)_buffer[0]) << 16 | (unsigned)(_buffer[1]) << 8 | _buffer[2];
unsigned blockId = (unsigned)(_buffer[0]) << 8 | _buffer[1];
return _decoder.decode(blockId, _buffer.data() + _headerSize, block_size());
}

View File

@ -10,7 +10,7 @@ template <unsigned _bufferSize> // 599
class fountain_encoder_stream
{
public:
static const unsigned _headerSize = 3;
static const unsigned _headerSize = 2;
protected:
fountain_encoder_stream(std::string&& data)
@ -66,9 +66,8 @@ public:
_encoder.encode(_block++, data, block_size()); // try twice -- the last initial block will be the wrong size
unsigned block = _block - 1;
_buffer.data()[0] = (block >> 16) & 0xFF;
_buffer.data()[1] = (block >> 8) & 0xFF;
_buffer.data()[2] = block & 0xFF;
_buffer.data()[0] = (block >> 8) & 0xFF;
_buffer.data()[1] = block & 0xFF;
_buffIndex = 0;
}

View File

@ -3,6 +3,8 @@
#include "FountainDecoder.h"
#include "FountainEncoder.h"
#include "serialize/format.h"
#include "base91/base.hpp"
#include <iostream>
#include <sstream>
#include <string>
@ -11,6 +13,18 @@
using std::string;
using namespace std;
namespace {
std::vector<std::string> blocks()
{
std::vector<std::string> precomp = {
R"(gw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77B)",
R"(b%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@hN7R;4!x;m?zs12+Xv$Sb%ob^heMAJ-:N[*T6texAkZdx2G?uCbRA2IbLmUuFKR*|jgSr!=EWo!G-1m/=*lT{+(abj5XgJ8=%N|QQztEml0o[2;(8YAS77Z.wnXB7IB.y6FTL)CF2i~RR2W-mCLUgw_,$k_i$Js@A)",
R"(c%Uhlr*CCfs)S[@q4^ELGi@D*2w[l$TsWuwP'mG44=-{`EM!`%Vsmoc=+Ts)%^_{8,QnK??D;$8=3Q%&juuPX?2op&:{vFE!{uj.+`T=JWM)*HtTE//mn}qCGf~=KWU%8^@JWi\pr}-@2$yn{um=-^c1h\S))H5nc%Uhlr*CCfs)S[@q4^ELGi@D*2w[l$TsWuwP'mG44=-{`EM!`%Vsmoc=+Ts)%^_{8,QnK??D;$8=3Q%&juuPX?2op&:{vFE!{uj.+`T=JWM)*HtTE//mn}qCGf~=KWU%8^@JWi\pr}-@2$yn{um=-^c1h\S))H5nc%Uhlr*CCfs)S[@q4^ELGi@D*2w[l$TsWuwP'mG44=-{`EM!`%Vsmoc=+Ts)%^_{8,QnK??D;$8=3Q%&juuPX?2op&:{vFE!{uj.+`T=JWM)*HtTE//mn}qCGf~=KWU%8^@JWi\pr}-@9j[%o[##DKr^90w^%3*:Oo;#9eG$=q'c5+Y;!gV-j[{ZpMCf(o?iu3.N:xGi`.qx'pR3TTYu!V-R|=%o\-BF){f,*p?.ilHU_-ePE=mVu7d@5lg9Km$`~5Vzv8i%o!*.]z]YE!r[(*'U2b3JN)=3^I'^gUnmuCir9j[%o[##DKr^90w^%3*:Oo;#9eG$=q'c5+Y;!gV-j[{ZpMCf(o?iu3.N:xGi`.qx'pR3TTYu!V-R|=%o\-BF){f,*p?.ilHU_-ePE=mVu7d@5lg9Km$`~5Vzv8i%o!*.]z]YE!r[(*'U2b3JN)=3^ID)"
};
return precomp;
}
}
TEST_CASE( "FountainEncodingTest/testEncoder", "[unit]" )
{
FountainEncoder::init();
@ -83,3 +97,81 @@ TEST_CASE( "FountainEncodingTest/testEncodingAndDecoding", "[unit]" )
assertEquals( 11, block_id );
assertEquals( 8, decoded_blocks );
}
TEST_CASE( "FountainEncodingTest/testConsistency", "[unit]" )
{
const std::vector<std::string> precomputed = blocks();
static const unsigned packetSize = 626;
FountainEncoder::init();
int messageSize = 1000;
std::string message;
while (message.size() < messageSize)
message += "0123456789";
message.resize(messageSize);
// make sure encoding is consistent
FountainEncoder encoder((uint8_t*)message.data(), message.size(), packetSize);
std::array<uint8_t,packetSize> block;
for (unsigned block_id = 0; block_id < 3; ++block_id)
{
unsigned bites = encoder.encode(block_id, block.data(), block.size());
std::string actual = base91::encode(std::string((char*)block.data(), bites));
//std::cout << block_id << " : " << actual << std::endl;
assertEquals( precomputed[block_id], actual );
}
// make sure decoding is consistent
FountainDecoder decoder(messageSize, packetSize);
for (unsigned block_id = 0; block_id < 3; ++block_id)
{
if (block_id == 1)
continue;
std::string block_data = base91::decode(precomputed[block_id]);
std::optional<vector<uint8_t>> res = decoder.decode(block_id, (uint8_t*)block_data.data(), block_data.size());
if (block_id == 2)
{
assertTrue( res );
std::string actual = std::string((char*)res->data(), res->size());
assertEquals( message, actual );
}
}
}
TEST_CASE( "FountainEncodingTest/testWhichN", "[unit]" )
{
const std::vector<std::string> precomputed = blocks();
static const unsigned packetSize = 624;
FountainEncoder::init();
int messageSize = 6000;
std::string message;
while (message.size() < messageSize)
message += "0123456789";
message.resize(messageSize);
// create encoder and decoder
FountainEncoder encoder((uint8_t*)message.data(), message.size(), packetSize);
FountainDecoder decoder(messageSize, packetSize);
std::array<uint8_t,packetSize> block;
std::vector<uint8_t> final_result;
// decode backwards, never using the "real" data blocks
int block_id = 105;
for (; block_id >= 0; block_id -= 1)
{
unsigned bites = encoder.encode(block_id, block.data(), block.size());
std::optional<vector<uint8_t>> res = decoder.decode(block_id, block.data(), bites);
if (res)
{
final_result = *res;
break;
}
}
assertEquals( 96, block_id );
assertEquals( message, string((char*)final_result.data(), final_result.size()) );
}

View File

@ -115,9 +115,8 @@ TEST_CASE( "FountainSinkTest/testMultipart", "[unit]" )
TEST_CASE( "FountainSinkTest/testSameFrameManyTimes", "[unit]" )
{
// this is the current test case for what is either a wirehair bug or "undefined behavior"
// tl;dr -- if you give it the same frame (under certain circumstances), you get a seg fault
// if you give wirehair the same frame (under certain circumstances), you get a seg fault
// sometimes it's fine. The docs say "don't do it", so FountainDecoder acts as the bouncer.
FountainInit::init();
fountain_decoder_sink<626> sink("/tmp");

View File

@ -59,12 +59,11 @@ TEST_CASE( "FountainStreamTest/testEncoder_BlockHeader", "[unit]" )
assertEquals( res, buff.size() );
assertEquals( 0, buff[0] );
assertEquals( 0, buff[1] );
if (i+1 >= fes.blocks_required())
assertEquals( i+1, buff[2] );
assertEquals( i+1, buff[1] );
else
assertEquals( i, buff[2] );
assertEquals( i, buff[1] );
}
assertEquals( 21, fes.block_count() );
@ -143,7 +142,7 @@ TEST_CASE( "FountainStreamTest/testDecode", "[unit]" )
assertMsg((bool)output, "couldn't decode :(");
}
assertEquals( 827, fds.block_size() );
assertEquals( 828, fds.block_size() );
assertEquals( 10000, fds.data_size() );
assertTrue( fds.good() );
@ -185,7 +184,7 @@ TEST_CASE( "FountainStreamTest/testDecode_BigPackets", "[unit]" )
assertMsg((bool)output, "couldn't decode :(");
}
assertEquals( 827, fds.block_size() );
assertEquals( 828, fds.block_size() );
assertEquals( 10000, fds.data_size() );
assertTrue( fds.good() );