/* * Copyright (c) 2015, Luca Fulchir, All rights reserved. * * This file is part of "libRaptorQ". * * libRaptorQ is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * libRaptorQ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * and a copy of the GNU Lesser General Public License * along with libRaptorQ. If not, see . */ #ifndef RAPTORQ_DECODER_HPP #define RAPTORQ_DECODER_HPP #include "common.hpp" #include "Parameters.hpp" #include "Precode_Matrix.hpp" #include "Graph.hpp" #include #include #include #include #include #include #include #include namespace RaptorQ { namespace Impl { template using Symbol = std::vector; template class RAPTORQ_LOCAL Decoder { public: Decoder (const uint16_t symbols, const uint16_t symbol_size) :_symbols (symbols), _symbol_size (symbol_size / sizeof(T)), precode (Parameters(symbols)), mask (_symbols) { static_assert(std::is_unsigned::value, "RaptorQ: Decoder can only be used with unsigned types"); // symbol size is in octets, but we save it in "T" sizes. // so be aware that "symbol_size" != "_symbol_size" for now source_symbols = DenseMtx (_symbols, _symbol_size * sizeof(T)); } bool add_symbol (const uint32_t esi, const std::vector &symbol); bool decode (); DenseMtx* get_symbols(); std::vector get (const uint16_t symbol) const; private: std::mutex lock; const uint16_t _symbols, _symbol_size; Precode_Matrix precode; Bitmask mask; DenseMtx source_symbols; std::vector>> received_repair; }; /////////////////////////////////// // // IMPLEMENTATION OF ABOVE TEMPLATE // /////////////////////////////////// template bool Decoder::add_symbol (const uint32_t esi, const std::vector &symbol) { // true if added succesfully if (symbol.size() != _symbol_size || esi >= std::pow (2, 20)) return false; std::lock_guard guard (lock); UNUSED(guard); if (mask.get_holes() == 0) return false; // not even needed; if (esi < _symbols) { if (mask.exists(esi)) return false; // already present. uint16_t col = 0; for (T al : symbol) { for (uint8_t *p = reinterpret_cast (&al); p != reinterpret_cast (&al) + sizeof(T); ++p) { source_symbols (esi, col++) = *p; } } mask.add (esi); } else { // FIXME: do *NOT* ad it twice, even if asked. received_repair.emplace_back (esi, symbol); } return true; } template bool Decoder::decode () { // rfc 6330: can decode when received >= K_padded // actually: (K_padded - K) are padding and thus constant and NOT // transmitted. so really N >= K. if (mask.get_holes() == 0) return true; if (received_repair.size() < mask.get_holes()) return false; precode.gen (received_repair.size() - mask.get_holes()); DenseMtx D = DenseMtx (precode._params.S + precode._params.H + precode._params.K_padded + (received_repair.size() - mask.get_holes()), source_symbols.cols()); // initialize D for (uint16_t row = 0; row < precode._params.S + precode._params.H; ++row) { for (uint16_t col = 0; col < D.cols(); ++col) D (row, col) = 0; } // put non-repair symbols (source symbols) in place lock.lock(); if (mask.get_holes() == 0) // other thread completed its work before us? return true; D.block (precode._params.S + precode._params.H, 0, source_symbols.rows(), D.cols()) = source_symbols; // mask must be copied to avoid threading problems, same with tracking // the repair esi. const Bitmask mask_safe = mask; std::vector repair_esi; repair_esi.reserve (received_repair.size()); for (auto rep : received_repair) repair_esi.push_back (rep.first); // fill holes with the first repair symbols available auto symbol = received_repair.begin(); for (uint16_t hole = 0; hole < _symbols && symbol != received_repair.end(); ++hole) { if (mask_safe.exists (hole)) continue; uint8_t col = 0; const int16_t row = precode._params.S + precode._params.H + hole; for (T al : symbol->second) { for (uint8_t *p = reinterpret_cast (&al); p != reinterpret_cast (&al) + sizeof(T); ++p, ++col) { D (row, col) = *p; } } ++symbol; } // fill the padding symbols (always zero) for (uint16_t row = precode._params.S + precode._params.H + _symbols; row < precode._params.S + precode._params.H + precode._params.K_padded; ++row) { for (uint16_t col = 0; col < D.cols(); ++col) D (row, col) = 0; } // fill the remaining (redundant) repair symbols for (uint16_t row = precode._params.S + precode._params.H + precode._params.K_padded; symbol != received_repair.end(); ++symbol, ++row) { uint8_t col = 0; for (T al : symbol->second) { for (uint8_t *p = reinterpret_cast (&al); p != reinterpret_cast (&al) + sizeof(T); ++p, ++col) { D (row, col) = *p; } } } lock.unlock(); // do not lock this part, as it's the expensive part precode.intermediate (D, mask_safe, repair_esi); if (D.rows() == 0) return mask.get_holes() == 0; // other threads did something? std::lock_guard guard (lock); UNUSED(guard); if (mask.get_holes() == 0) return true; // other thread did something)) // put missing symbols into "source_symbols". // remember: we might have received other symbols while decoding. uint16_t D_row = 0; for (uint16_t row = 0; row < mask._max; ++row) { if (mask_safe.exists (row)) continue; ++D_row; if (mask.exists (row)) continue; source_symbols.row (row) = D.row (D_row - 1); mask.add (row); } if (received_repair.capacity() != 0) { // free some memory, we don't need recover symbols anymore received_repair = std::vector>>(); } return true; } template std::vector Decoder::get (const uint16_t symbol) const { std::vector ret; if (mask.get_holes() != 0 || symbol >= source_symbols.rows()) return ret; for (uint16_t col = 0; col < source_symbols.cols(); ++col) { T al; for (uint8_t *p = reinterpret_cast (&al); p != reinterpret_cast (&al) + sizeof(T); ++p) { *p = static_cast (source_symbols (symbol, col)); ++col; } --col; ret.push_back (al); } return ret; } template DenseMtx* Decoder::get_symbols() { return &source_symbols; } } //namespace Impl } // namespace RaptorQ #endif