TEST(TestReedSolomonCodes, test_encode_decode)
{
    {
        kodo::rs_encoder<fifi::binary8>::factory encoder_factory(255, 1600);
        kodo::rs_decoder<fifi::binary8>::factory decoder_factory(255, 1600);

        uint32_t symbols = rand_symbols(255);
        uint32_t symbol_size = rand_symbol_size();

        encoder_factory.set_symbols(symbols);
        encoder_factory.set_symbol_size(symbol_size);

        decoder_factory.set_symbols(symbols);
        decoder_factory.set_symbol_size(symbol_size);

        auto encoder = encoder_factory.build();
        auto decoder = decoder_factory.build();

        // Encode/decode operations
        EXPECT_TRUE(encoder->payload_size() == decoder->payload_size());

        std::vector<uint8_t> payload(encoder->payload_size());
        std::vector<uint8_t> data_in = random_vector(encoder->block_size());

        encoder->set_symbols(sak::storage(data_in));

        uint32_t symbol_count = 0;
        while( !decoder->is_complete() )
        {
            encoder->encode( &payload[0] );
            decoder->decode( &payload[0] );
            ++symbol_count;
        }

        // A reed solomon code should be able to decode
        // with exactly k symbols
        EXPECT_EQ(symbol_count, symbols);

        std::vector<uint8_t> data_out(decoder->block_size(), '\0');
        decoder->copy_symbols(sak::storage(data_out));

        EXPECT_TRUE(std::equal(data_out.begin(),
                               data_out.end(),
                               data_in.begin()));

    }

}
Beispiel #2
0
int main()
{

    // Set the number of symbols (i.e. the generation size in RLNC
    // terminology) and the size of a symbol in bytes
    uint32_t max_symbols = 42;
    uint32_t max_symbol_size = 64;

    std::string encode_filename = "encode-file.bin";

    // Create a test file for encoding.
    std::ofstream encode_file;
    encode_file.open (encode_filename, std::ios::binary);

    uint32_t file_size = 50000;
    std::vector<char> encode_data(file_size);
    std::vector<char> decode_data;

    // Just write some bytes to the file
    for(uint32_t i = 0; i < file_size; ++i)
    {
        encode_data[i] = rand() % 255;
    }
    encode_file.write(&encode_data[0], file_size);
    encode_file.close();

    // Select the encoding and decoding algorithms
    typedef kodo::full_rlnc_encoder<fifi::binary>
        encoder_t;

    typedef kodo::full_rlnc_decoder<fifi::binary>
        decoder_t;

    // Now for the encoder we use a file_encoder with the chosen
    // encoding algorithm
    typedef kodo::file_encoder<encoder_t>
        file_encoder_t;

    // For decoding we use an object_decoder with the chosen
    // decoding algorithm
    typedef kodo::object_decoder<decoder_t>
        object_decoder_t;

    // Create the encoder factory - builds the individual encoders used
    file_encoder_t::factory encoder_factory(max_symbols, max_symbol_size);

    // Create the actual file encoder using the encoder factory and
    // the filename of the file to be encoded
    file_encoder_t file_encoder(encoder_factory, encode_filename);

    // Create the decoder factory - build the individual decoders used
    object_decoder_t::factory decoder_factory(max_symbols, max_symbol_size);

    // Create the object decoder using the decoder factory and the
    // size of the file to be decoded
    object_decoder_t object_decoder(decoder_factory, file_size);

    // Now in the following loop we go through all the encoders
    // needed to encode the entire file. We the build the corresponding
    // decoder and decode the chunk immediately. In practice where
    // encoders and decoders are on different devices e.g. connected
    // over a network, we would have to pass also the encoder and decoder
    // index between the source and sink to allow the correct data would
    // passed from encoder to corresponding decoder.
    for(uint32_t i = 0; i < file_encoder.encoders(); ++i)
    {
        auto encoder = file_encoder.build(i);
        auto decoder = object_decoder.build(i);

        // Set the encoder non-systematic
        if(kodo::has_systematic_encoder<encoder_t>::value)
            kodo::set_systematic_off(encoder);

        std::vector<uint8_t> payload(encoder->payload_size());

        while( !decoder->is_complete() )
        {
            // Encode a packet into the payload buffer
            encoder->encode( &payload[0] );

            // In practice send the payload over a network, save it to
            // a file etc. Then when needed build and pass it to the decoder

            // Pass that packet to the decoder
            decoder->decode( &payload[0] );
        }

        std::vector<uint8_t> data_out(decoder->block_size());
        decoder->copy_symbols(sak::storage(data_out));
        data_out.resize(decoder->bytes_used());

        decode_data.insert(decode_data.end(),
                           data_out.begin(),
                           data_out.end());
    }

    // Check we properly decoded the data
    if (std::equal(decode_data.begin(),
                   decode_data.end(), encode_data.begin()))
    {
        std::cout << "Data decoded correctly" << std::endl;
    }
    else
    {
        std::cout << "Unexpected failure to decode "
                  << "please file a bug report :)" << std::endl;
    }
}
inline void test_basic_api(uint32_t symbols, uint32_t symbol_size)
{

    // Common setting
    typename Encoder::factory encoder_factory(symbols, symbol_size);
    auto encoder = encoder_factory.build();

    typename Decoder::factory decoder_factory(symbols, symbol_size);
    auto decoder = decoder_factory.build();

    EXPECT_TRUE(symbols == encoder_factory.max_symbols());
    EXPECT_TRUE(symbol_size == encoder_factory.max_symbol_size());
    EXPECT_TRUE(symbols == encoder->symbols());
    EXPECT_TRUE(symbol_size == encoder->symbol_size());

    EXPECT_TRUE(symbols == decoder_factory.max_symbols());
    EXPECT_TRUE(symbol_size == decoder_factory.max_symbol_size());
    EXPECT_TRUE(symbols == decoder->symbols());
    EXPECT_TRUE(symbol_size == decoder->symbol_size());

    EXPECT_TRUE(encoder->symbol_length() > 0);
    EXPECT_TRUE(decoder->symbol_length() > 0);

    EXPECT_TRUE(encoder->block_size() == symbols * symbol_size);
    EXPECT_TRUE(decoder->block_size() == symbols * symbol_size);

    EXPECT_TRUE(encoder_factory.max_payload_size() >=
                encoder->payload_size());

    EXPECT_TRUE(decoder_factory.max_payload_size() >=
                decoder->payload_size());

    EXPECT_EQ(encoder_factory.max_payload_size(),
              decoder_factory.max_payload_size());

    // Encode/decode operations
    EXPECT_EQ(encoder->payload_size(), decoder->payload_size());

    std::vector<uint8_t> payload(encoder->payload_size());

    std::vector<uint8_t> data_in = random_vector(encoder->block_size());
    std::vector<uint8_t> data_in_copy(data_in);

    sak::mutable_storage storage_in = sak::storage(data_in);
    sak::mutable_storage storage_in_copy = sak::storage(data_in_copy);

    EXPECT_TRUE(sak::equal(storage_in, storage_in_copy));

    // Only used for prime fields, lets reconsider how we implement
    // this less intrusive
    uint32_t prefix = 0;

    if(fifi::is_prime2325<typename Encoder::field_type>::value)
    {
        // This field only works for multiple of uint32_t
        assert((encoder->block_size() % 4) == 0);

        uint32_t block_length = encoder->block_size() / 4;

        fifi::prime2325_binary_search search(block_length);
        prefix = search.find_prefix(storage_in_copy);

        // Apply the negated prefix
        fifi::apply_prefix(storage_in_copy, ~prefix);
    }

    encoder->set_symbols(storage_in_copy);

    // Set the encoder non-systematic
    if(kodo::is_systematic_encoder(encoder))
        kodo::set_systematic_off(encoder);

    while( !decoder->is_complete() )
    {
        uint32_t payload_used = encoder->encode( &payload[0] );
        EXPECT_TRUE(payload_used <= encoder->payload_size());

        decoder->decode( &payload[0] );
    }

    std::vector<uint8_t> data_out(decoder->block_size(), '\0');
    decoder->copy_symbols(sak::storage(data_out));

    if(fifi::is_prime2325<typename Encoder::field_type>::value)
    {
        // Now we have to apply the negated prefix to the decoded data
        fifi::apply_prefix(sak::storage(data_out), ~prefix);
    }

    EXPECT_TRUE(std::equal(data_out.begin(),
                           data_out.end(),
                           data_in.begin()));
}
Beispiel #4
0
inline void
invoke_reuse_incomplete(uint32_t symbols, uint32_t symbol_size)
{

    bool do_complete;

    typename Encoder::factory encoder_factory(symbols, symbol_size);
    typename Decoder::factory decoder_factory(symbols, symbol_size);

    // Use factory a lot of times
    for (uint32_t i = 0; i < 100; ++i)
    {
        // Build coders
        auto encoder = encoder_factory.build();
        auto decoder = decoder_factory.build();

        // Prepare buffers
        std::vector<uint8_t> payload(encoder->payload_size());
        std::vector<uint8_t> data_in(encoder->block_size());

        // Fill with random data
        for (auto &e: data_in)
            e = rand() % 256;

        // Put data in encoder
        encoder->set_symbols(sak::storage(data_in));

        if (rand() % 100 > 90)
        {
            do_complete = false;
        }
        else
        {
            do_complete = true;
        }

        // Start encoding/decoding
        while (!decoder->is_complete())
        {
            encoder->encode(&payload[0]);

            // Loose a packet with probability
            if (rand() % 100 > 90)
                continue;

            decoder->decode(&payload[0]);

            // Stop decoding after a while with probability
            if (!do_complete && decoder->rank() == symbols - 2)
                break;
        }

        // Check if completed decoders are correct
        if (decoder->is_complete())
        {
            std::vector<uint8_t> data_out(decoder->block_size());
            decoder->copy_symbols(sak::storage(data_out));

            ASSERT_TRUE(sak::equal(sak::storage(data_out),
                                   sak::storage(data_in)));
        }
    }

}
/// @example use_cached_symbol_decoder.cpp
///
/// This example shows how to use the cached symbol decoder to "extract"
/// the symbol coding coefficients and the encoded symbol data from an
/// incoming symbol.
int main()
{
    // The finite field we will use in the example. You can try
    // with other fields by specifying e.g. fifi::binary8 for the
    // extension field 2^8
    typedef fifi::binary finite_field;

    // Set the number of symbols (i.e. the generation size in RLNC
    // terminology) and the size of a symbol in bytes
    uint32_t symbols = 8;
    uint32_t symbol_size = 160;

    // Typdefs for the encoder/decoder type we wish to use
    typedef kodo::full_rlnc_encoder<finite_field> rlnc_encoder;
    typedef kodo::full_rlnc_decoder<finite_field> rlnc_decoder;

    typedef kodo::symbol_info_decoder<finite_field> rlnc_info_decoder;

    // In the following we will make an encoder/decoder factory.
    // The factories are used to build actual encoders/decoders.
    // Each stack we use have their own factories.
    rlnc_encoder::factory encoder_factory(symbols, symbol_size);
    auto encoder = encoder_factory.build();

    rlnc_decoder::factory decoder_factory(symbols, symbol_size);
    auto decoder = decoder_factory.build();

    rlnc_info_decoder::factory info_decoder_factory(symbols, symbol_size);
    auto info_decoder = info_decoder_factory.build();

    // Allocate some storage for a "payload" the payload is what we would
    // eventually send over a network
    std::vector<uint8_t> payload(encoder->payload_size());

    // Allocate some data to encode. In this case we make a buffer
    // with the same size as the encoder's block size (the max.
    // amount a single encoder can encode)
    std::vector<uint8_t> data_in(encoder->block_size());

    // Just for fun - fill the data with random data
    for(auto &e: data_in)
        e = rand() % 256;

    // Assign the data buffer to the encoder so that we may start
    // to produce encoded symbols from it
    encoder->set_symbols(sak::storage(data_in));

    while( !decoder->is_complete())
    {
        // Encode a packet into the payload buffer
        encoder->encode( &payload[0] );

        // Here we "simulate" a packet loss of approximately 50%
        // by dropping half of the encoded packets.
        // When running this example you will notice that the initial
        // symbols are received systematically (i.e. uncoded). After
        // sending all symbols once uncoded, the encoder will switch
        // to full coding, in which case you will see the full encoding
        // vectors being sent and received.
        if((rand() % 2) == 0)
            continue;

        // Pass the encoded packet to the info decoder. After this
        // information about the coded symbol can be fetched using the
        // cached_symbol_decoder API
        info_decoder->decode( &payload[0] );

        if(!info_decoder->cached_symbol_coded())
        {
            // The symbol was uncoded so we may ask the cache which of the
            // original symbols we have received.

            std::cout << "Symbol was uncoded, index = "
                      << info_decoder->cached_symbol_index() << std::endl;

            // Now we pass the data directly into our actual decoder. This is
            // done using the "Codec API" directly, and not through the "Payload
            // API" as we would typically do.
            decoder->decode_symbol( info_decoder->cached_symbol_data(),
                                    info_decoder->cached_symbol_index());

        }
        else
        {
            // The symbol was coded so we may ask the cache to return
            // the coding coefficients used to create the encoded symbol.

            std::cout << "Symbol was coded, encoding vector = ";

            const uint8_t* c = info_decoder->cached_symbol_coefficients();

            // We loop through the coefficient buffer and print the coefficients
            for(uint32_t i = 0; i < info_decoder->symbols(); ++i)
            {
                std::cout << (uint32_t) fifi::get_value<finite_field>(c, i)
                          << " ";
            }

            std::cout << std::endl;

            // Pass that packet to the decoder, as with the uncoded symbols
            // above we pass it directly to the "Codec API"
            decoder->decode_symbol(info_decoder->cached_symbol_data(),
                                   info_decoder->cached_symbol_coefficients());

        }
    }

    // The decoder is complete, now copy the symbols from the decoder
    std::vector<uint8_t> data_out(decoder->block_size());
    decoder->copy_symbols(sak::storage(data_out));

    // Check we properly decoded the data
    if (std::equal(data_out.begin(), data_out.end(), data_in.begin()))
    {
        std::cout << "Data decoded correctly" << std::endl;
    }
    else
    {
        std::cout << "Unexpected failure to decode "
                  << "please file a bug report :)" << std::endl;
    }
}
inline void run_test_on_the_fly_systematic_no_errors(uint32_t symbols,
                                                 uint32_t symbol_size)
{
    // Common setting
    typename Encoder::factory encoder_factory(symbols, symbol_size);
    auto encoder = encoder_factory.build();

    typename Decoder::factory decoder_factory(symbols, symbol_size);
    auto decoder = decoder_factory.build();

    // Encode/decode operations
    EXPECT_TRUE(encoder->payload_size() == decoder->payload_size());

    std::vector<uint8_t> payload(encoder->payload_size());
    std::vector<uint8_t> data_in = random_vector(encoder->block_size());

    auto symbol_sequence = sak::split_storage(
        sak::storage(data_in), symbol_size);

    // Make sure the encoder is systematic
    if(kodo::has_systematic_encoder<Encoder>::value)
        kodo::set_systematic_on(encoder);

    while( !decoder->is_complete() )
    {
        encoder->encode( &payload[0] );
        decoder->decode( &payload[0] );

        if(decoder->rank() > 0)
        {
            EXPECT_TRUE(kodo::is_partial_complete(decoder));
        }

        if(kodo::is_partial_complete(decoder))
        {
            // Check that we as many pivot elements as expected and that these
            // are decoded
            uint32_t symbols_uncoded = 0;
            for(uint32_t i = 0; i < decoder->symbols(); ++i)
            {
                if(!decoder->is_symbol_uncoded(i))
                    continue;

                ++symbols_uncoded;

                auto symbol_storage =
                    sak::storage(decoder->symbol(i), decoder->symbol_size());

                EXPECT_TRUE(sak::is_equal(symbol_storage, symbol_sequence[i]));
            }

            EXPECT_EQ(symbols_uncoded, decoder->symbols_uncoded());
        }


        // set symbol 50% of the time ONLY if rank is not full
        if(encoder->rank() < symbols)
        {
            uint32_t i = encoder->rank();
            encoder->set_symbol(i, symbol_sequence[i]);
        }

        EXPECT_TRUE(encoder->rank() >= decoder->rank());
    }

    std::vector<uint8_t> data_out(decoder->block_size(), '\0');
    decoder->copy_symbols(sak::storage(data_out));

    EXPECT_TRUE(std::equal(data_out.begin(),
                           data_out.end(),
                           data_in.begin()));

}
inline void run_test_on_the_fly(uint32_t symbols, uint32_t symbol_size)
{

    // Common setting
    typename Encoder::factory encoder_factory(symbols, symbol_size);
    auto encoder = encoder_factory.build();

    typename Decoder::factory decoder_factory(symbols, symbol_size);
    auto decoder = decoder_factory.build();

    std::vector<uint8_t> payload(encoder->payload_size());
    std::vector<uint8_t> data_in = random_vector(encoder->block_size());

    auto symbol_sequence = sak::split_storage(
        sak::storage(data_in), symbol_size);

    // Set the encoder non-systematic
    if(kodo::has_systematic_encoder<Encoder>::value)
        kodo::set_systematic_off(encoder);

    EXPECT_EQ(encoder->rank(), 0U);
    EXPECT_EQ(decoder->rank(), 0U);

    while( !decoder->is_complete() )
    {
        EXPECT_TRUE(encoder->rank() >= decoder->rank());

        encoder->encode( &payload[0] );

        // Simulate some loss
        if((rand() % 2) == 0)
            continue;

        decoder->decode( &payload[0] );

        if(kodo::is_partial_complete(decoder))
        {
            // Check that we as many pivot elements as expected and that these
            // are decoded
            uint32_t symbols_uncoded = 0;
            for(uint32_t i = 0; i < decoder->symbols(); ++i)
            {
                if(!decoder->is_symbol_uncoded(i))
                    continue;

                ++symbols_uncoded;

                auto symbol_storage =
                    sak::storage(decoder->symbol(i), decoder->symbol_size());

                EXPECT_TRUE(sak::is_equal(symbol_storage, symbol_sequence[i]));
            }

            EXPECT_EQ(symbols_uncoded, decoder->symbols_uncoded());
        }

        if(((rand() % 2) == 0) && encoder->rank() < symbols)
        {
            uint32_t i = encoder->rank();
            encoder->set_symbol(i, symbol_sequence[i]);
        }
    }

    std::vector<uint8_t> data_out(decoder->block_size(), '\0');
    decoder->copy_symbols(sak::storage(data_out));

    EXPECT_TRUE(std::equal(data_out.begin(),
                           data_out.end(),
                           data_in.begin()));
}
inline void run_test_on_the_fly_systematic(uint32_t symbols, uint32_t symbol_size)
{
    // Common setting
    typename Encoder::factory encoder_factory(symbols, symbol_size);
    auto encoder = encoder_factory.build();

    typename Decoder::factory decoder_factory(symbols, symbol_size);
    auto decoder = decoder_factory.build();

    // Encode/decode operations
    EXPECT_TRUE(encoder->payload_size() == decoder->payload_size());

    std::vector<uint8_t> payload(encoder->payload_size());
    std::vector<uint8_t> data_in = random_vector(encoder->block_size());

    auto symbol_sequence = sak::split_storage(
        sak::storage(data_in), symbol_size);

    // Make sure the encoder is systematic
    if(kodo::has_systematic_encoder<Encoder>::value)
        kodo::set_systematic_on(encoder);


    while( !decoder->is_complete() )
    {
        encoder->encode( &payload[0] );

        // Simulate some loss
        if((rand() % 2) == 0)
            continue;

        uint32_t old_rank = decoder->rank();

        decoder->decode( &payload[0] );

        if(decoder->rank() > old_rank)
        {
            // We has a rank increase
            if(encoder->rank() == decoder->rank())
            {
                // The rank of the encoder matches the decoder, if
                // this unit test fails you most likely do not have a
                // layer which updates the decoding symbol status when
                // rank matches.  E.g. look at:
                // rank_symbol_decoding_status_updater.hpp
                EXPECT_TRUE(kodo::is_partial_complete(decoder));
            }
        }

        if(kodo::is_partial_complete(decoder))
        {
            // Check that we as many pivot elements as expected and that these
            // are decoded
            uint32_t symbols_uncoded = 0;
            for(uint32_t i = 0; i < decoder->symbols(); ++i)
            {
                if(!decoder->is_symbol_uncoded(i))
                    continue;

                ++symbols_uncoded;

                auto symbol_storage =
                    sak::storage(decoder->symbol(i), decoder->symbol_size());

                EXPECT_TRUE(sak::is_equal(symbol_storage, symbol_sequence[i]));
            }

            EXPECT_EQ(symbols_uncoded, decoder->symbols_uncoded());
        }


        // set symbol 50% of the time ONLY if rank is not full
        if(((rand() % 2) == 0) && (encoder->rank() < symbols))
        {
            uint32_t i = encoder->rank();
            encoder->set_symbol(i, symbol_sequence[i]);
        }

        EXPECT_TRUE(encoder->rank() >= decoder->rank());
    }

    std::vector<uint8_t> data_out(decoder->block_size(), '\0');
    decoder->copy_symbols(sak::storage(data_out));

    EXPECT_TRUE(std::equal(data_out.begin(),
                           data_out.end(),
                           data_in.begin()));

}
inline void invoke_recoding(recoding_parameters param)
{
    // Common setting
    typename Encoder::factory encoder_factory(
        param.m_max_symbols, param.m_max_symbol_size);

    encoder_factory.set_symbols(param.m_symbols);
    encoder_factory.set_symbol_size(param.m_symbol_size);

    auto encoder = encoder_factory.build();

    typename Decoder::factory decoder_factory(
        param.m_max_symbols, param.m_max_symbol_size);

    decoder_factory.set_symbols(param.m_symbols);
    decoder_factory.set_symbol_size(param.m_symbol_size);

    auto decoder_one = decoder_factory.build();
    auto decoder_two = decoder_factory.build();

    // If tested with a shallow decoder we have to remember to set the
    // buffers to use for the decoding
    std::vector<uint8_t> buffer_decoder_one(decoder_one->block_size(), '\0');
    std::vector<uint8_t> buffer_decoder_two(decoder_two->block_size(), '\0');

    if(kodo::has_shallow_symbol_storage<Decoder>::value)
    {
        decoder_one->set_symbols(sak::storage(buffer_decoder_one));
        decoder_two->set_symbols(sak::storage(buffer_decoder_two));
    }

    EXPECT_EQ(encoder->payload_size(), decoder_one->payload_size());
    EXPECT_EQ(encoder->payload_size(), decoder_two->payload_size());

    std::vector<uint8_t> payload(encoder->payload_size());
    std::vector<uint8_t> data_in = random_vector(encoder->block_size());

    encoder->set_symbols(sak::storage(data_in));

    // Set the encoder non-systematic
    if(kodo::has_systematic_encoder<Encoder>::value)
        kodo::set_systematic_off(encoder);

    while( !decoder_two->is_complete() )
    {
        uint32_t encode_size = encoder->encode( &payload[0] );
        EXPECT_TRUE(encode_size <= payload.size());
        EXPECT_TRUE(encode_size > 0);

        decoder_one->decode( &payload[0] );

        uint32_t recode_size = decoder_one->recode( &payload[0] );
        EXPECT_TRUE(recode_size <= payload.size());
        EXPECT_TRUE(recode_size > 0);

        decoder_two->decode( &payload[0] );
    }

    std::vector<uint8_t> data_out_one(decoder_one->block_size(), '\0');
    std::vector<uint8_t> data_out_two(decoder_two->block_size(), '\0');

    decoder_one->copy_symbols(sak::storage(data_out_one));
    decoder_two->copy_symbols(sak::storage(data_out_two));

    EXPECT_TRUE(std::equal(data_out_one.begin(),
                           data_out_one.end(),
                           data_in.begin()));

    EXPECT_TRUE(std::equal(data_out_two.begin(),
                           data_out_two.end(),
                           data_in.begin()));
}
Beispiel #10
0
// Tests that encoding and decoding a file withe the file encoder
// works.
TEST(TestFileEncoder, test_file_encoder)
{

    {
        std::string encode_filename = "encode-file";
        std::string decode_filename = "decode-file";

        // Write a test file
        std::ofstream encode_file;
        encode_file.open (encode_filename, std::ios::binary);

        uint32_t size = 500;
        for(uint32_t i = 0; i < size; ++i)
        {
            char c = rand() % 255;
            encode_file.write(&c, 1);
        }

        encode_file.close();

        typedef kodo::full_rlnc_encoder<fifi::binary>
            encoder_t;

        typedef kodo::full_rlnc_decoder<fifi::binary>
            decoder_t;

        typedef kodo::file_encoder<encoder_t>
            file_encoder_t;

        typedef kodo::object_decoder<decoder_t>
            object_decoder_t;

        uint32_t max_symbols = 10;
        uint32_t max_symbol_size = 10;

        file_encoder_t::factory encoder_factory(
            max_symbols, max_symbol_size);

        file_encoder_t file_encoder(encoder_factory, encode_filename);

        object_decoder_t::factory decoder_factory(
            max_symbols, max_symbol_size);

        object_decoder_t object_decoder(decoder_factory, size);

        EXPECT_EQ(object_decoder.decoders(), file_encoder.encoders());

        // Open the decode file
        std::ofstream decode_file;
        decode_file.open (decode_filename, std::ios::binary);

        for(uint32_t i = 0; i < file_encoder.encoders(); ++i)
        {
            auto encoder = file_encoder.build(i);
            auto decoder = object_decoder.build(i);

            EXPECT_EQ(encoder->symbols(), decoder->symbols());
            EXPECT_EQ(encoder->symbol_size(), decoder->symbol_size());
            EXPECT_EQ(encoder->bytes_used(), decoder->bytes_used());

            // Set the encoder non-systematic
            if(kodo::is_systematic_encoder(encoder))
                kodo::set_systematic_off(encoder);

            std::vector<uint8_t> payload(encoder->payload_size());

            while( !decoder->is_complete() )
            {
                // Encode a packet into the payload buffer
                encoder->encode( &payload[0] );

                // Pass that packet to the decoder
                decoder->decode( &payload[0] );
            }

            std::vector<uint8_t> data_out(decoder->block_size());
            decoder->copy_symbols(sak::storage(data_out));

            decode_file.write(
                reinterpret_cast<char*>(&data_out[0]), decoder->bytes_used());

        }

        decode_file.close();

    }

}