Code Example: Save/Restore Decoder

In this example shows how to use the API of otacast to implement save and restore of a decoder. This allows the decoder to be shutdown and then later continue from where ever it left off.

  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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iostream>
#include <random>
#include <vector>

#include <otacast/decoder.hpp>
#include <otacast/encoder.hpp>
#include <otacast/to_string.hpp>
#include <otacast/version.hpp>

bool file_exists(const std::string filename)
{
    return std::ifstream(filename).good();
}

std::vector<uint8_t> read(const std::string filename)
{
    std::vector<uint8_t> buffer;
    std::ifstream in(
        filename, std::ifstream::ate | std::ios::out | std::ios::binary);
    buffer.resize(in.tellg());
    in.seekg(0);
    in.read((char*)buffer.data(), buffer.size());
    return buffer;
}

void write(const std::string filename, const uint8_t* data, std::size_t size)
{
    std::ofstream(filename, std::ios::out | std::ios::binary)
        .write((char*)data, size);
}

std::string decoder_state_filename(uint64_t files)
{
    return "decoder.state." + std::to_string(files);
}

uint64_t decoder_state_files()
{
    uint64_t files = 0;
    while (file_exists(decoder_state_filename(files + 1)))
    {
        files++;
    }
    return files;
}

int main()
{
    /* using nano-seconds instead of seconds */
    srand(std::chrono::system_clock::now().time_since_epoch().count());

    // Create an encoder and decoder
    otacast::encoder encoder;
    otacast::decoder decoder;

    // Pick the number of bytes to encode/decode
    auto block_bytes = 100000;

    // Pick the size of each symbol in bytes
    auto symbol_bytes = 1400;

    // Pick the encoding width. The higher the width, the higher the chance that
    // an encoded packet is useful, but also increase the complexity of both the
    // encoding and the decoding
    auto width = 20;

    // Pick the finite field to use for the encoding and decoding
    auto field = otacast::finite_field::binary8;

    // Configure the encoder
    encoder.configure(field, block_bytes, symbol_bytes, width);

    // Allocate some storage for a symbol
    std::vector<uint8_t> symbol(encoder.symbol_bytes());

    // Allocate some data to encode. In this case we make a buffer with the same
    // size as the encoder's blocksize (the max. amount a single encoder can
    // encode)
    std::vector<uint8_t> data_in;
    if (file_exists("data.in"))
    {
        data_in = read("data.in");
    }
    else
    {
        data_in.resize(encoder.block_bytes());
        // Just for fun - fill data_in with random data
        std::generate(data_in.begin(), data_in.end(), rand);
        write("data.in", data_in.data(), data_in.size());
    }

    // Assign the data buffer to the encoder so that we may start to produce
    // encoded symbols from it
    encoder.set_symbols_storage(data_in.data());

    // Configure the decoder to some previous configurations or define it like
    // the current encoder
    auto state_files = decoder_state_files();
    if (state_files != 0)
    {
        std::vector<uint8_t> state = read(decoder_state_filename(state_files));
        auto result = decoder.restore_state(state.data(), state.size());
        if (!result)
        {
            std::cout << "decoder state corrupted, "
                      << "please delete decoder.state" << std::endl;
            return 1;
        }
    }
    else
    {
        decoder.configure(field, block_bytes, symbol_bytes, width);
    }

    // Define a data buffer for the decoder
    std::vector<uint8_t> data_out;
    if (file_exists("data.out"))
    {
        data_out = read("data.out");
    }
    else
    {
        data_out.resize(decoder.block_bytes());
    }

    decoder.set_symbols_storage(data_out.data());

    auto old_progress = decoder.progress();

    auto seed = rand();
    auto offset = encoder.encode(symbol.data(), seed);
    std::cout << "offset: " << offset << " seed: " << seed << std::endl;
    decoder.decode(symbol.data(), seed, offset);

    if (decoder.can_complete_decoding())
        decoder.complete_decoding();

    if (decoder.is_complete())
    {
        if (data_out == data_in)
        {
            std::cout << "Data decoded correctly" << std::endl;
        }
        else
        {
            std::cout << "Unexpected failure to decode, "
                      << "please file a bug report :)" << std::endl;
            return 1;
        }
    }
    else
    {
        std::cout << "Progress: " << decoder.progress() << "%.";
        if (decoder.progress() == old_progress)
        {
            std::cout << " packet linearly dependent!" << std::endl;
            return 0;
        }
        std::cout << std::endl;

        std::vector<uint8_t> state(decoder.state_bytes());
        decoder.save_state(state.data());
        write(
            decoder_state_filename(state_files + 1), state.data(),
            state.size());
        write("data.out", data_out.data(), data_out.size());
    }
    return 0;
}