Encode on-the-flyΒΆ

This example shows how to to perform on-the-fly encoding. The encoder can start to generate encoded packets from a block before all symbols are specified. This can be useful when the symbols are produced on-the-fly.

The complete example code is shown below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright Steinwurf ApS 2011.
// Distributed under the "STEINWURF EVALUATION LICENSE 1.0".
// See accompanying file LICENSE.rst or
// http://www.steinwurf.com/licensing

#include <algorithm>
#include <ctime>
#include <iostream>
#include <set>
#include <string>
#include <vector>

#include <storage/split.hpp>

#include <kodo_rlnc/coders.hpp>

/// @example encode_on_the_fly.cpp
///
/// This example shows how to perform on-the-fly encoding. The encoder
/// can start to generate encoded packets from a block before all symbols
/// are specified. This can be useful when the symbols are produced on-the-fly.

int main()
{
    // Seed random number generator to produce different results every time
    srand(static_cast<uint32_t>(time(0)));

    // Set the number of symbols (i.e. the generation size in RLNC
    // terminology) and the size of a symbol in bytes
    uint32_t symbols = 6;
    uint32_t symbol_size = 160;
    fifi::api::field field = fifi::api::field::binary8;

    // Typdefs for the encoder/decoder type we wish to use
    using rlnc_encoder = kodo_rlnc::encoder;
    using rlnc_decoder = kodo_rlnc::decoder;

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

    rlnc_decoder::factory decoder_factory(field, symbols, symbol_size);
    auto decoder = 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
    std::generate(data_in.begin(), data_in.end(), rand);

    // Let's split the data into symbols and feed the encoder one symbol
    // at a time
    auto symbol_storage =
        storage::split(storage::storage(data_in), symbol_size);

    // Define a data buffer where the symbols should be decoded
    std::vector<uint8_t> data_out(decoder->block_size());
    decoder->set_mutable_symbols(storage::storage(data_out));

    // Install a trace function for the decoder if tracing is enabled
    if (decoder->has_trace_support())
    {
        auto callback = [](const std::string& zone, const std::string& data)
        {
            std::set<std::string> filters =
                { "decoder_state", "input_symbol_coefficients" };

            if (filters.count(zone))
            {
                std::cout << zone << ":" << std::endl;
                std::cout << data << std::endl;
            }
        };

        decoder->set_trace_callback(callback);
    }

    while (!decoder->is_complete())
    {
        // Randomly choose to insert a symbol until the encoder is full
        if ((rand() % 2) && (encoder->rank() < symbols))
        {
            // The rank of an encoder specifies the number of symbols
            // it has available for encoding
            uint32_t rank = encoder->rank();

            encoder->set_const_symbol(rank, symbol_storage[rank]);

            std::cout << "Symbol " << rank << " added to the encoder"
                      << std::endl;
        }

        if (encoder->rank() == 0)
        {
            continue;
        }

        // Write an encoded packet into the payload buffer
        encoder->write_payload(payload.data());

        std::cout << "Encoded packet generated" << std::endl;

        // Send the data to the decoder, here we just for fun
        // simulate that we are losing 50% of the packets
        if (rand() % 2)
        {
            std::cout << "Packet dropped on channel" << std::endl << std::endl;
            continue;
        }

        std::cout << "Decoder received packet" << std::endl;

        // Packet got through - pass that packet to the decoder
        decoder->read_payload(payload.data());

        std::cout << "Encoder rank = " << encoder->rank() << std::endl;
        std::cout << "Decoder rank = " << decoder->rank() << std::endl;
        std::cout << "Decoder uncoded = " << decoder->symbols_uncoded()
                  << " / symbols: ";
        for (uint32_t i = 0; i < decoder->symbols(); ++i)
        {
            if (decoder->is_symbol_uncoded(i))
                std::cout << i << " ";
        }
        std::cout << std::endl;
        std::cout << "Decoder partially decoded = "
                  << decoder->symbols_partially_decoded() << std::endl
                  << std::endl;
    }

    // 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;
    }
}