Skip to content
RaptorQ.hpp 22.8 KiB
Newer Older
Luker's avatar
Luker committed
/*
Luker's avatar
Luker committed
 * Copyright (c) 2015-2016, Luca Fulchir<luca@fulchir.it>, All rights reserved.
Luker's avatar
Luker committed
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

Luker's avatar
Luker committed
#pragma once
Luker's avatar
Luker committed

/////////////////////
//
//	These templates are just a wrapper around the
Luker's avatar
Luker committed
//	functionalities offered by the RaptorQ__v1::Impl namespace
Luker's avatar
Luker committed
//	So if you want to see what the algorithm looks like,
//	you are in the wrong place
//
/////////////////////

#include "Interleaver.hpp"
#include "De_Interleaver.hpp"
#include "Encoder.hpp"
#include "Decoder.hpp"
#include "Shared_Computation/Decaying_LF.hpp"
Luker's avatar
Luker committed
#include <map>
#include <mutex>
#include <thread>
#include <type_traits>
#include <utility>

Luker's avatar
Luker committed
namespace RaptorQ__v1 {
Luker's avatar
Luker committed

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Encoder;

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Symbol
{
public:
Luker's avatar
Luker committed
	Symbol (Encoder<Rnd_It, Fwd_It> *enc, const uint32_t esi, const uint8_t sbn)
Luker's avatar
Luker committed
		: _enc (enc), _esi (esi), _sbn (sbn) {}

Luker's avatar
Luker committed
	uint64_t operator() (Fwd_It &start, const Fwd_It end)
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return _enc->encode (start, end, _esi, _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	uint32_t id() const
	{
		uint32_t ret = _sbn;
		ret <<= 24;
Luker's avatar
Luker committed
		return ret + _esi;
Luker's avatar
Luker committed
private:
Luker's avatar
Luker committed
	Encoder<Rnd_It, Fwd_It> *_enc;
Luker's avatar
Luker committed
	const uint32_t _esi;
	const uint8_t _sbn;
};
Luker's avatar
Luker committed

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Symbol_Iterator :
Luker's avatar
Luker committed
		public std::iterator<std::input_iterator_tag, Symbol<Rnd_It, Fwd_It>>
Luker's avatar
Luker committed
{
public:
Luker's avatar
Luker committed
	Symbol_Iterator (Encoder<Rnd_It, Fwd_It> *enc, const uint32_t esi,
Luker's avatar
Luker committed
															const uint8_t sbn)
Luker's avatar
Luker committed
		: _enc (enc), _esi (esi), _sbn (sbn) {}
Luker's avatar
Luker committed
	Symbol<Rnd_It, Fwd_It> operator*()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return Symbol<Rnd_It, Fwd_It> (_enc, _esi, _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Symbol_Iterator<Rnd_It, Fwd_It>& operator++()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		++_esi;
		return *this;
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Symbol_Iterator operator++ (const int i) const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		Symbol_Iterator<Rnd_It, Fwd_It> ret (_esi + i, _sbn);
Luker's avatar
Luker committed
		return ret;
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	bool operator== (const Symbol_Iterator<Rnd_It, Fwd_It> &it) const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return it._esi == _esi && it._sbn == _sbn;
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	bool operator!= (const Symbol_Iterator<Rnd_It, Fwd_It> &it) const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return it._esi != _esi || it._sbn != _sbn;
	}
private:
Luker's avatar
Luker committed
	Encoder<Rnd_It, Fwd_It> *_enc;
Luker's avatar
Luker committed
	uint32_t _esi;
	const uint8_t _sbn;
};

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Block
{
public:
Luker's avatar
Luker committed
	Block (Encoder<Rnd_It, Fwd_It> *enc, const uint16_t symbols,
Luker's avatar
Luker committed
															const uint8_t sbn)
Luker's avatar
Luker committed
		: _enc (enc), _symbols (symbols), _sbn (sbn) {}

Luker's avatar
Luker committed
	Symbol_Iterator<Rnd_It, Fwd_It> begin_source() const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return Symbol_Iterator<Rnd_It, Fwd_It> (_enc, 0, _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Symbol_Iterator<Rnd_It, Fwd_It> end_source() const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return Symbol_Iterator<Rnd_It, Fwd_It> (_enc, _symbols, _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Symbol_Iterator<Rnd_It, Fwd_It> begin_repair() const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return Symbol_Iterator<Rnd_It, Fwd_It> (_enc, _symbols, _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Symbol_Iterator<Rnd_It, Fwd_It> end_repair (const uint32_t max_repair)
Luker's avatar
Luker committed
																		const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		uint32_t max_r = max_repair;
Luker's avatar
Luker committed
		uint32_t max_sym = 1;
		max_sym <<= 20;	// max_sym = 2^20
Luker's avatar
Luker committed
		if (max_repair > std::pow (2, 20) - _symbols)
Luker's avatar
Luker committed
			max_r = max_sym - _symbols;
Luker's avatar
Luker committed
		return Symbol_Iterator<Rnd_It, Fwd_It> (_enc, _symbols + max_r,_sbn);
Luker's avatar
Luker committed
	}
	uint32_t max_repair() const
Luker's avatar
Luker committed
	{
		return _enc->max_repair (_sbn);
	}
	uint16_t symbols () const
	{
		return _enc->symbols (_sbn);
	}
	uint32_t block_size () const
	{
		return _enc->block_size (_sbn);
	}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
private:
Luker's avatar
Luker committed
	Encoder<Rnd_It, Fwd_It> * _enc;
Luker's avatar
Luker committed
	const uint16_t _symbols;
Luker's avatar
Luker committed
	const uint8_t _sbn;
};

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Block_Iterator :
Luker's avatar
Luker committed
		public std::iterator<std::input_iterator_tag, Block<Rnd_It, Fwd_It>>
Luker's avatar
Luker committed
{
public:
Luker's avatar
Luker committed
	Block_Iterator (Encoder<Rnd_It, Fwd_It> *enc, const Impl::Partition part,
Luker's avatar
Luker committed
																uint8_t sbn)
Luker's avatar
Luker committed
		:_enc (enc), _part (part), _sbn (sbn) {}
Luker's avatar
Luker committed
	Block<Rnd_It, Fwd_It> operator*()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		if (_sbn < _part.num (0))
Luker's avatar
Luker committed
			return Block<Rnd_It, Fwd_It> (_enc, _part.size (0), _sbn);
		return Block<Rnd_It, Fwd_It> (_enc, _part.size (1), _sbn);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Block_Iterator& operator++()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		++_sbn;
		return *this;
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	Block_Iterator operator++ (const int i) const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		Block_Iterator ret = *this;
		ret._sbn += i;
		return ret;
	}
	bool operator== (const Block_Iterator &it) const
Luker's avatar
Luker committed
	{
		return it._sbn == _sbn;
Luker's avatar
Luker committed
	}
	bool operator!= (const Block_Iterator &it) const
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return it._sbn != _sbn;
Luker's avatar
Luker committed
	}
private:
Luker's avatar
Luker committed
	Encoder<Rnd_It, Fwd_It> *_enc;
Luker's avatar
Luker committed
	const Impl::Partition _part;
Luker's avatar
Luker committed
	uint8_t _sbn;
};


uint64_t shared_cache_size (const uint64_t shared_cache);
bool local_cache_size (const uint64_t local_cache);
uint64_t get_shared_cache_size();
uint64_t get_local_cache_size();


static const uint64_t max_data = 946270874880;	// ~881 GB
Luker's avatar
Luker committed

Luker's avatar
Luker committed
typedef uint64_t OTI_Common_Data;
typedef uint32_t OTI_Scheme_Specific_Data;

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Encoder
{
public:

Luker's avatar
Luker committed
	~Encoder();
Luker's avatar
Luker committed
	Encoder (const Rnd_It data_from, const Rnd_It data_to,
Luker's avatar
Luker committed
											const uint16_t min_subsymbol_size,
											const uint16_t symbol_size,
											const size_t max_memory)
Luker's avatar
Luker committed
		: _mem (max_memory), _data_from (data_from), _data_to (data_to),
Luker's avatar
Luker committed
											_symbol_size (symbol_size),
Luker's avatar
Luker committed
											_min_subsymbol (min_subsymbol_size)
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		IS_RANDOM(Rnd_It, "RaptorQ__v1::Encoder");
		IS_FORWARD(Fwd_It, "RaptorQ__v1::Encoder");
		auto _alignment = sizeof(typename
									std::iterator_traits<Rnd_It>::value_type);
Luker's avatar
Luker committed
		UNUSED(_alignment);	// used only for asserts
		assert(_symbol_size >= _alignment &&
						"RaptorQ: symbol_size must be >= alignment");
		assert((_symbol_size % _alignment) == 0 &&
						"RaptorQ: symbol_size must be multiple of alignment");
		assert(min_subsymbol_size >= _alignment &&
						"RaptorQ: minimum subsymbol must be at least aligment");
		assert(min_subsymbol_size <= _symbol_size &&
Luker's avatar
Luker committed
					"RaptorQ: minimum subsymbol must be at most symbol_size");
		assert((min_subsymbol_size % _alignment) == 0 &&
					"RaptorQ: minimum subsymbol must be multiple of alignment");
Luker's avatar
Luker committed
		assert((_symbol_size % min_subsymbol_size == 0) &&
					"RaptorQ: symbol size must be multiple of subsymbol size");
		// max size: ~881 GB
Luker's avatar
Luker committed
		if (static_cast<uint64_t> (data_to - data_from) *
					sizeof(typename std::iterator_traits<Rnd_It>::value_type)
																> max_data) {
Luker's avatar
Luker committed
			return;
Luker's avatar
Luker committed
		}
Luker's avatar
Luker committed
		interleave = std::unique_ptr<Impl::Interleaver<Rnd_It>> (
								new Impl::Interleaver<Rnd_It> (_data_from,
														_data_to,
Luker's avatar
Luker committed
														_min_subsymbol, _mem,
Luker's avatar
Luker committed
														_symbol_size));
		if (!(*interleave))
			interleave = nullptr;
Luker's avatar
Luker committed
	}

Luker's avatar
Luker committed
	Block_Iterator<Rnd_It, Fwd_It> begin ()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		return Block_Iterator<Rnd_It, Fwd_It> (this,
Luker's avatar
Luker committed
												interleave->get_partition(), 0);
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed
	const Block_Iterator<Rnd_It, Fwd_It> end ()
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		auto part = interleave->get_partition();
Luker's avatar
Luker committed
		return Block_Iterator<Rnd_It, Fwd_It> (this, part,
Luker's avatar
Luker committed
							static_cast<uint8_t> (part.num(0) + part.num(1)));
Luker's avatar
Luker committed
	}
Luker's avatar
Luker committed

	operator bool() const { return interleave != nullptr; }
Luker's avatar
Luker committed
	OTI_Common_Data OTI_Common() const;
	OTI_Scheme_Specific_Data OTI_Scheme_Specific() const;

Luker's avatar
Luker committed
	void precompute (const uint8_t threads, const bool background);
Luker's avatar
Luker committed
	size_t precompute_max_memory ();
Luker's avatar
Luker committed
	uint64_t encode (Fwd_It &output, const Fwd_It end, const uint32_t esi,
Luker's avatar
Luker committed
															const uint8_t sbn);
Luker's avatar
Luker committed
	// id: 8-bit sbn + 24 bit esi
Luker's avatar
Luker committed
	uint64_t encode (Fwd_It &output, const Fwd_It end, const uint32_t &id);
Luker's avatar
Luker committed
	void free (const uint8_t sbn);
Luker's avatar
Luker committed
	uint8_t blocks() const;
	uint32_t block_size (const uint8_t sbn) const;
	uint16_t symbol_size() const;
	uint16_t symbols (const uint8_t sbn) const;
	uint32_t max_repair (const uint8_t sbn) const;
Luker's avatar
Luker committed
private:
Luker's avatar
Luker committed
	class RAPTORQ_LOCAL Locked_Encoder
	{
	public:
Luker's avatar
Luker committed
		Locked_Encoder (const Impl::Interleaver<Rnd_It> &symbols,
															const uint8_t SBN)
Luker's avatar
Luker committed
			:_enc (symbols, SBN)
		{}
		std::mutex _mtx;
Luker's avatar
Luker committed
		Impl::Encoder<Rnd_It, Fwd_It> _enc;
Luker's avatar
Luker committed
	};
Luker's avatar
Luker committed
	std::vector<std::thread> background_work;
Luker's avatar
Luker committed
	std::unique_ptr<Impl::Interleaver<Rnd_It>> interleave = nullptr;
Luker's avatar
Luker committed
	std::map<uint8_t, std::shared_ptr<Locked_Encoder>> encoders;
Luker's avatar
Luker committed
	std::mutex _mtx;
Luker's avatar
Luker committed
	const size_t _mem;
	const Rnd_It _data_from, _data_to;
	const uint16_t _symbol_size;
Luker's avatar
Luker committed
	const uint16_t _min_subsymbol;

Luker's avatar
Luker committed
	static void precompute_block_all (Encoder<Rnd_It, Fwd_It> *obj,
Luker's avatar
Luker committed
														const uint8_t threads);
Luker's avatar
Luker committed
	static void precompute_thread (Encoder<Rnd_It, Fwd_It> *obj, uint8_t *sbn,
													const uint8_t single_sbn);
Luker's avatar
Luker committed
};


Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
Luker's avatar
Luker committed
class RAPTORQ_API Decoder
{
public:
Luker's avatar
Luker committed
	// rfc 6330, pg 6
	// easy explanation for OTI_* comes next.
	// we do NOT use bitfields as compilators are not actually forced to put
	// them in any particular order. meaning tey're useless.
	//
	//union OTI_Common_Data {
	//	uint64_t raw;
	//	struct {
	//		uint64_t size:40;
	//		uint8_t reserved:8;
	//		uint16_t symbol_size:16;
	//	};
	//};

	//union OTI_Scheme_Specific_Data {
	//	uint32_t raw;
	//	struct {
	//		uint8_t source_blocks;
	//		uint16_t sub_blocks;
	//		uint8_t	alignment;
	//	};
	//};
	Decoder (const OTI_Common_Data common,const OTI_Scheme_Specific_Data scheme)
Luker's avatar
Luker committed
	{
Luker's avatar
Luker committed
		IS_INPUT(In_It, "RaptorQ__v1::Decoder");
		IS_FORWARD(Fwd_It, "RaptorQ__v1::Decoder");
Luker's avatar
Luker committed
		// see the above commented bitfields for quick reference
		_symbol_size = static_cast<uint16_t> (common);
		uint16_t tot_sub_blocks = static_cast<uint16_t> (scheme >> 8);
		_alignment = static_cast<uint8_t> (scheme);
		_sub_blocks = Impl::Partition (_symbol_size /
												static_cast<uint8_t> (scheme),
																tot_sub_blocks);
		_blocks = static_cast<uint8_t> (scheme >> 24);
		_size = common >> 24;
Luker's avatar
Luker committed
		//	(common >> 24) == total file size
		if (_size > max_data)
Luker's avatar
Luker committed
			return;

		const uint64_t total_symbols = static_cast<uint64_t> (ceil (
								_size / static_cast<double> (_symbol_size)));
Luker's avatar
Luker committed

		part = Impl::Partition (total_symbols, static_cast<uint8_t> (_blocks));
Luker's avatar
Luker committed
	}

	Decoder (const uint64_t size, const uint16_t symbol_size,
													const uint16_t sub_blocks,
													const uint8_t blocks,
													const uint8_t alignment)
		:_size (size), _symbol_size (symbol_size), _blocks (blocks),
														_alignment(alignment)
		if (_size > max_data)
			return;

		const uint64_t total_symbols = static_cast<uint64_t> (ceil (
								_size / static_cast<double> (_symbol_size)));
		_sub_blocks = Impl::Partition (_symbol_size / _alignment, sub_blocks);

		part = Impl::Partition (total_symbols, static_cast<uint8_t> (_blocks));
	}
Luker's avatar
Luker committed

	bool precompute ();
Luker's avatar
Luker committed
	uint64_t decode (Fwd_It &start, const Fwd_It end);
	uint64_t decode (Fwd_It &start, const Fwd_It end, const uint8_t sbn);
Luker's avatar
Luker committed
	// id: 8-bit sbn + 24 bit esi
	Error add_symbol (In_It &start, const In_It end, const uint32_t id);
	Error add_symbol (In_It &start, const In_It end, const uint32_t esi,
Luker's avatar
Luker committed
															const uint8_t sbn);
	void free (const uint8_t sbn);
	uint64_t bytes() const;
Luker's avatar
Luker committed
	uint8_t blocks() const;
	uint32_t block_size (const uint8_t sbn) const;
	uint16_t symbol_size() const;
	uint16_t symbols (const uint8_t sbn) const;
Luker's avatar
Luker committed
private:
	// using shared pointers to avoid locking too much or
	// worrying about deleting used stuff.
Luker's avatar
Luker committed
	using Dec_ptr = std::shared_ptr<RaptorQ__v1::Impl::Decoder<In_It>>;
	uint64_t _size;
	Impl::Partition part, _sub_blocks;
	uint16_t _symbol_size;
	uint8_t _blocks, _alignment;
Luker's avatar
Luker committed
	std::map<uint8_t, Dec_ptr> decoders;
	std::mutex _mtx;
};





/////////////////
//
// Encoder
//
/////////////////
Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
Encoder<Rnd_It, Fwd_It>::~Encoder ()
{
	for (auto &thread: background_work)
		thread.join();
}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
OTI_Common_Data Encoder<Rnd_It, Fwd_It>::OTI_Common() const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	OTI_Common_Data ret;
	// first 40 bits: data length.
	ret = (static_cast<uint64_t> (_data_to - _data_from) *
			sizeof(typename std::iterator_traits<Rnd_It>::value_type)) << 24;
Luker's avatar
Luker committed
	// 8 bits: reserved
	// last 16 bits: symbol size
Luker's avatar
Luker committed
	ret += _symbol_size;
Luker's avatar
Luker committed

	return ret;
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
OTI_Scheme_Specific_Data Encoder<Rnd_It, Fwd_It>::OTI_Scheme_Specific() const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	OTI_Scheme_Specific_Data ret;
	// 8 bit: source blocks
Luker's avatar
Luker committed
	ret = static_cast<uint32_t> (interleave->blocks()) << 24;
Luker's avatar
Luker committed
	// 16 bit: sub-blocks number (N)
Luker's avatar
Luker committed
	ret += static_cast<uint32_t> (interleave->sub_blocks()) << 8;
Luker's avatar
Luker committed
	// 8 bit: alignment
Luker's avatar
Luker committed
	ret += sizeof(typename std::iterator_traits<Rnd_It>::value_type);
Luker's avatar
Luker committed

	return ret;
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
size_t Encoder<Rnd_It, Fwd_It>::precompute_max_memory ()
Luker's avatar
Luker committed
{
	// give a good estimate on the amount of memory neede for the precomputation
	// of one block;
	// this will help you understand how many concurrent precomputations
	// you want to do :)

	if (interleave == nullptr)
		return 0;

	uint16_t symbols = interleave->source_symbols(0);

	uint16_t K_idx;
	for (K_idx = 0; K_idx < Impl::K_padded.size(); ++K_idx) {
		if (symbols < Impl::K_padded[K_idx])
			break;
	}
	if (K_idx == Impl::K_padded.size())
		return 0;

	auto S_H = Impl::S_H_W[K_idx];
	uint16_t matrix_cols = Impl::K_padded[K_idx] + std::get<0> (S_H) +
															std::get<1> (S_H);

Luker's avatar
Luker committed
	// Rough memory estimate: Matrix A, matrix X (=> *2) and matrix D.
	return matrix_cols * matrix_cols * 2 + _symbol_size * matrix_cols;
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
void Encoder<Rnd_It, Fwd_It>::precompute_thread (Encoder<Rnd_It, Fwd_It> *obj,
Luker's avatar
Luker committed
													uint8_t *sbn,
													const uint8_t single_sbn)
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (obj->interleave == nullptr)
		return;
Luker's avatar
Luker committed
	// if "sbn" pointer is NOT nullptr, than we are a thread from
	// from a precompute_block_all. This means that we need to update
	// the value of sbn as soon as we get our work.
	//
	// if sbn == nullptr, then we have been called to work on a single
	// sbn, and not from "precompute_block_all".
	// This means we work on "single_sbn", and do not touch "sbn"

	uint8_t *sbn_ptr = sbn;
	if (sbn_ptr == nullptr)
		sbn_ptr = const_cast<uint8_t*> (&single_sbn);
Luker's avatar
Luker committed
	// call this from a thread, precomput all block symbols
Luker's avatar
Luker committed
	while (*sbn_ptr < obj->interleave->blocks()) {
		obj->_mtx.lock();
		if (*sbn_ptr >= obj->interleave->blocks()) {
			obj->_mtx.unlock();
			return;
		}
		auto it = obj->encoders.find (*sbn_ptr);
		if (it == obj->encoders.end()) {
Luker's avatar
Luker committed
			bool success;
Luker's avatar
Luker committed
			std::tie (it, success) = obj->encoders.insert ({*sbn_ptr,
Luker's avatar
Luker committed
					std::make_shared<Locked_Encoder> (*obj->interleave,*sbn_ptr)
Luker's avatar
Luker committed
														});
Luker's avatar
Luker committed
		}
		auto enc_ptr = it->second;
Luker's avatar
Luker committed
		bool locked = enc_ptr->_mtx.try_lock();
		if (sbn != nullptr)
			++(*sbn);
		obj->_mtx.unlock();
Luker's avatar
Luker committed
		if (locked) {	// if not locked, someone else is already waiting
						// on this. so don't do the same work twice.
Luker's avatar
Luker committed
			// result can be nullptr. in that case, disregard the result.
			enc_ptr->_enc.generate_symbols();
Luker's avatar
Luker committed
			enc_ptr->_mtx.unlock();
Luker's avatar
Luker committed
		}
Luker's avatar
Luker committed
		if (sbn == nullptr)
			return;
Luker's avatar
Luker committed
	}
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
void Encoder<Rnd_It, Fwd_It>::precompute (const uint8_t threads,
Luker's avatar
Luker committed
														const bool background)
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return;
Luker's avatar
Luker committed
	if (background) {
Luker's avatar
Luker committed
		background_work.emplace_back (precompute_block_all, this, threads);
Luker's avatar
Luker committed
	} else {
		return precompute_block_all (this, threads);
	}
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
void Encoder<Rnd_It, Fwd_It>::precompute_block_all (
												Encoder<Rnd_It, Fwd_It> *obj,
Luker's avatar
Luker committed
												const uint8_t threads)
Luker's avatar
Luker committed
{
	// precompute all intermediate symbols, do it with more threads.
Luker's avatar
Luker committed
	if (obj->interleave == nullptr)
Luker's avatar
Luker committed
		return;
	std::vector<std::thread> t;
Luker's avatar
Luker committed
	int32_t spawned = threads - 1;
Luker's avatar
Luker committed
	if (spawned <= -1)
Luker's avatar
Luker committed
		spawned = static_cast<int32_t> (std::thread::hardware_concurrency());
Luker's avatar
Luker committed

	if (spawned > 0)
		t.reserve (static_cast<size_t> (spawned));
Luker's avatar
Luker committed
	uint8_t sbn = 0;

	// spawn n-1 threads
	for (int8_t id = 0; id < spawned; ++id)
		t.emplace_back (precompute_thread, obj, &sbn, 0);
Luker's avatar
Luker committed

Luker's avatar
Luker committed
	// do the work ourselves
	precompute_thread (obj, &sbn, 0);
Luker's avatar
Luker committed

	// join other threads
	for (uint8_t id = 0; id < spawned; ++id)
		t[id].join();
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint64_t Encoder<Rnd_It, Fwd_It>::encode (Fwd_It &output, const Fwd_It end,
Luker's avatar
Luker committed
															const uint32_t &id)
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	const uint32_t mask_8 = static_cast<uint32_t> (std::pow (2, 8)) - 1;
	const uint32_t mask = ~(mask_8 << 24);
Luker's avatar
Luker committed

Luker's avatar
Luker committed
	return encode (output, end, id & mask, static_cast<uint8_t> (id & mask_8));
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint64_t Encoder<Rnd_It, Fwd_It>::encode (Fwd_It &output, const Fwd_It end,
Luker's avatar
Luker committed
															const uint32_t esi,
															const uint8_t sbn)
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	if (sbn >= interleave->blocks())
Luker's avatar
Luker committed
		return 0;
Luker's avatar
Luker committed
	_mtx.lock();
	auto it = encoders.find (sbn);
	if (it == encoders.end()) {
		bool success;
Luker's avatar
Luker committed
		std::tie (it, success) = encoders.emplace (sbn,
						std::make_shared<Locked_Encoder> (*interleave, sbn));
		background_work.emplace_back (precompute_thread, this, nullptr, sbn);
Luker's avatar
Luker committed
	}
	auto enc_ptr = it->second;
	_mtx.unlock();
Luker's avatar
Luker committed
	if (esi >= interleave->source_symbols (sbn)) {
		// make sure we generated the intermediate symbols
		enc_ptr->_mtx.lock();
		enc_ptr->_enc.generate_symbols();
Luker's avatar
Luker committed
		enc_ptr->_mtx.unlock();
	}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
	return enc_ptr->_enc.Enc (esi, output, end);
Luker's avatar
Luker committed
}


Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
void Encoder<Rnd_It, Fwd_It>::free (const uint8_t sbn)
Luker's avatar
Luker committed
{
	_mtx.lock();
	auto it = encoders.find (sbn);
	if (it != encoders.end())
		encoders.erase (it);
	_mtx.unlock();
}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint8_t Encoder<Rnd_It, Fwd_It>::blocks() const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	return interleave->blocks();
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint32_t Encoder<Rnd_It, Fwd_It>::block_size (const uint8_t sbn) const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	return interleave->source_symbols (sbn) * interleave->symbol_size();
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint16_t Encoder<Rnd_It, Fwd_It>::symbol_size() const
Luker's avatar
Luker committed
{
	if (interleave == nullptr)
		return 0;
	return interleave->symbol_size();
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint16_t Encoder<Rnd_It, Fwd_It>::symbols (const uint8_t sbn) const
Luker's avatar
Luker committed
{
	if (interleave == nullptr)
		return 0;
	return interleave->source_symbols (sbn);
}

Luker's avatar
Luker committed
template <typename Rnd_It, typename Fwd_It>
uint32_t Encoder<Rnd_It, Fwd_It>::max_repair (const uint8_t sbn) const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	if (interleave == nullptr)
		return 0;
Luker's avatar
Luker committed
	return static_cast<uint32_t> (std::pow (2, 20)) -
											interleave->source_symbols (sbn);
Luker's avatar
Luker committed
}
Luker's avatar
Luker committed
/////////////////
//
// Decoder
//
/////////////////

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
void Decoder<In_It, Fwd_It>::free (const uint8_t sbn)
Luker's avatar
Luker committed
{
	_mtx.lock();
	auto it = decoders.find(sbn);
	if (it != decoders.end())
		decoders.erase(it);
	_mtx.unlock();
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
Error Decoder<In_It, Fwd_It>::add_symbol (In_It &start, const In_It end,
Luker's avatar
Luker committed
															const uint32_t id)
{
Luker's avatar
Luker committed
	uint32_t esi = (id << 8 ) >> 8;
	uint8_t sbn = id >> 24;
Luker's avatar
Luker committed

Luker's avatar
Luker committed
	return add_symbol (start, end, esi, sbn);
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
Error Decoder<In_It, Fwd_It>::add_symbol (In_It &start, const In_It end,
										const uint32_t esi, const uint8_t sbn)
Luker's avatar
Luker committed
{
	if (sbn >= _blocks)
		return Error::WRONG_INPUT;
Luker's avatar
Luker committed
	_mtx.lock();
	auto it = decoders.find (sbn);
	if (it == decoders.end()) {
		const uint16_t symbols = sbn < part.num (0) ?
													part.size(0) : part.size(1);
Luker's avatar
Luker committed
		decoders.insert ({sbn, std::make_shared<Impl::Decoder<In_It>> (
Luker's avatar
Luker committed
													symbols, _symbol_size)});
		it = decoders.find (sbn);
	}
	auto dec = it->second;
	_mtx.unlock();

Luker's avatar
Luker committed
	return dec->add_symbol (start, end, esi);
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
bool Decoder<In_It, Fwd_It>::precompute ()
Luker's avatar
Luker committed
{
	_mtx.lock();
	auto it = decoders.find (0);
	if (it == decoders.end()) {
		_mtx.unlock();
		return false;
Luker's avatar
Luker committed
	}
	auto dec = it->second;
	_mtx.unlock();

	return dec->decode();
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint64_t Decoder<In_It, Fwd_It>::decode (Fwd_It &start, const Fwd_It end)
Luker's avatar
Luker committed
{
	for (uint8_t sbn = 0; sbn < _blocks; ++sbn) {
		_mtx.lock();
		auto it = decoders.find (sbn);
		if (it == decoders.end()) {
Luker's avatar
Luker committed
			_mtx.unlock();
			return 0;
Luker's avatar
Luker committed
		}
		auto dec = it->second;
		_mtx.unlock();

		if (!dec->decode())
Luker's avatar
Luker committed
			return 0;
	}
Luker's avatar
Luker committed
	// everything has been decoded
Luker's avatar
Luker committed
	// now try to decode all blocks. Some tricks are needed since the output
	// alignment and the block size might not be the same.
Luker's avatar
Luker committed
	uint64_t written = 0;
Luker's avatar
Luker committed
	uint8_t skip = 0;
Luker's avatar
Luker committed
	for (uint8_t sbn = 0; sbn < _blocks; ++sbn) {
		_mtx.lock();
		auto it = decoders.find (sbn);
		if (it == decoders.end())
			return written;
		auto dec = it->second;
		_mtx.unlock();
Luker's avatar
Luker committed
		Impl::De_Interleaver<Fwd_It> de_interleaving (dec->get_symbols(),
													_sub_blocks, _alignment);
Luker's avatar
Luker committed
		auto tmp_start = start;
		uint64_t tmp_written = de_interleaving (tmp_start, end, skip);
		if (tmp_written == 0)
			return written;
		written += tmp_written;
		// did we end up in the middle of a Fwd_It now?
		skip = block_size (sbn) %
					sizeof(typename std::iterator_traits<Fwd_It>::value_type);
		// if we ended decoding in the middle of a Fwd_It, do not advance
		// start too much, or we will end up having additional zeros.
		if (skip == 0) {
			start = tmp_start;
		} else {
			// tmp_written > 0 due to earlier return
			// moreover, RaptorQ handles at most 881GB per rfc, so
			// casting uint64 to int64 is safe
			// we can not do "--start" since it's a forward iterator
			start += static_cast<int64_t> (tmp_written - 1);
		}
Luker's avatar
Luker committed
	}
	return written;
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint64_t Decoder<In_It, Fwd_It>::decode (Fwd_It &start, const Fwd_It end,
Luker's avatar
Luker committed
															const uint8_t sbn)
Luker's avatar
Luker committed
{
	if (sbn >= _blocks)
		return 0;

	_mtx.lock();
	auto it = decoders.find (sbn);
	if (it == decoders.end()) {
		_mtx.unlock();
		return 0;
	}
	auto dec = it->second;
	_mtx.unlock();

	if (!dec->decode())
Luker's avatar
Luker committed
		return 0;

Luker's avatar
Luker committed
	Impl::De_Interleaver<Fwd_It> de_interleaving (dec->get_symbols(),
													_sub_blocks, _alignment);
Luker's avatar
Luker committed
	return de_interleaving (start, end);
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint64_t Decoder<In_It, Fwd_It>::bytes() const
Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint8_t Decoder<In_It, Fwd_It>::blocks() const
Luker's avatar
Luker committed
{
Luker's avatar
Luker committed
	return static_cast<uint8_t> (part.num (0) + part.num (1));
Luker's avatar
Luker committed
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint32_t Decoder<In_It, Fwd_It>::block_size (const uint8_t sbn) const
Luker's avatar
Luker committed
{
	if (sbn < part.num (0)) {
		return part.size (0) * _symbol_size;
	} else if (sbn - part.num (0) < part.num (1)) {
		return part.size (1) * _symbol_size;
	}
	return 0;
}

Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint16_t Decoder<In_It, Fwd_It>::symbol_size() const
Luker's avatar
Luker committed
{
	return _symbol_size;
}
Luker's avatar
Luker committed
template <typename In_It, typename Fwd_It>
uint16_t Decoder<In_It, Fwd_It>::symbols (const uint8_t sbn) const
Luker's avatar
Luker committed
{
	if (sbn < part.num (0)) {
		return part.size (0);
	} else if (sbn - part.num (0) < part.num (1)) {
		return part.size (1);
	}
	return 0;
}
Luker's avatar
Luker committed

Luker's avatar
Luker committed
}	// RaptorQ__v1