25 #define OPENSSL_SUPPRESS_DEPRECATED 1
26 #include <openssl/md5.h>
30 #include "spdlog/spdlog.h"
37 : _meta( std::move(entry) )
38 , _received_at( time(nullptr) )
41 spdlog::debug(
"Creating File from FileEntry");
43 spdlog::debug(
"Allocating buffer");
45 if (_buffer ==
nullptr)
47 throw "Failed to allocate file buffer";
51 this->calculate_partitioning();
52 this->create_blocks();
55 File::File(
const std::shared_ptr<Transmitter::FileDescription> &file_description)
57 , _file_description(file_description)
59 spdlog::debug(
"Creating File from FileDescription");
61 auto length = _file_description->data_length();
62 _buffer = (
char*)malloc(
length);
63 if (_buffer ==
nullptr)
65 throw "No data allocated";
68 memcpy(_buffer, _file_description->data(),
length);
69 _meta = _file_description->file_entry();
75 throw "Unsupported FEC scheme";
80 calculate_partitioning();
86 std::string content_location,
87 std::string content_type,
96 spdlog::debug(
"Creating File from data");
98 spdlog::debug(
"Allocating buffer");
99 _buffer = (
char*)malloc(
length);
100 if (_buffer ==
nullptr)
102 throw "Failed to allocate file buffer";
104 memcpy(_buffer, data,
length);
110 unsigned char md5[MD5_DIGEST_LENGTH];
111 MD5((
const unsigned char*)data,
length, md5);
117 _meta.
content_md5 = base64_encode(md5, MD5_DIGEST_LENGTH);
125 throw "Unsupported FEC scheme";
128 this->calculate_partitioning();
129 this->create_blocks();
134 spdlog::debug(
"Destroying File");
135 if (_own_buffer && _buffer !=
nullptr)
137 spdlog::debug(
"Freeing buffer");
144 if (symbol.source_block_number() > _source_blocks.size()) {
145 throw "Source Block number too high";
148 SourceBlock& source_block = _source_blocks[ symbol.source_block_number() ];
150 if (symbol.id() > source_block.symbols.size()) {
151 throw "Encoding Symbol ID too high";
157 symbol.decode_to(target_symbol.
data, target_symbol.
length);
160 check_source_block_completion(source_block);
161 check_file_completion();
166 auto File::check_source_block_completion( SourceBlock& block ) ->
void
168 block.complete = std::all_of(block.symbols.begin(), block.symbols.end(), [](
const auto& symbol){ return symbol.second.complete; });
171 auto File::check_file_completion() ->
void
173 _complete = std::all_of(_source_blocks.begin(), _source_blocks.end(), [](
const auto& block){ return block.second.complete; });
175 if (_complete && !_meta.content_md5.empty() && _meta.content_encoding.empty()) {
177 unsigned char md5[MD5_DIGEST_LENGTH];
178 MD5((
const unsigned char*)buffer(), length(), md5);
180 auto content_md5 = base64_decode(_meta.content_md5);
181 if (memcmp(md5, content_md5.c_str(), MD5_DIGEST_LENGTH) != 0) {
182 spdlog::debug(
"MD5 mismatch for TOI {}, discarding", _meta.toi);
185 for (
auto& block : _source_blocks) {
186 for (
auto& symbol : block.second.symbols) {
187 symbol.second.complete =
false;
189 block.second.complete =
false;
196 auto File::calculate_partitioning() ->
void
199 _nof_source_symbols = ceil((
double)_meta.fec_oti.transfer_length / (
double)_meta.fec_oti.encoding_symbol_length);
200 _nof_source_blocks = ceil((
double)_nof_source_symbols / (
double)_meta.fec_oti.max_source_block_length);
201 _large_source_block_length = ceil((
double)_nof_source_symbols / (
double)_nof_source_blocks);
202 _small_source_block_length = floor((
double)_nof_source_symbols / (
double)_nof_source_blocks);
203 _nof_large_source_blocks = _nof_source_symbols - _small_source_block_length * _nof_source_blocks;
206 auto File::create_blocks() ->
void
209 auto buffer_ptr = _buffer;
210 size_t remaining_size = _meta.fec_oti.transfer_length;
211 decltype(_nof_large_source_blocks) number = 0;
212 while (remaining_size > 0) {
214 size_t symbol_id = 0;
215 auto block_length = ( number < _nof_large_source_blocks ) ? _large_source_block_length : _small_source_block_length;
217 for (decltype(block_length) i = 0; i < block_length; i++) {
218 auto symbol_length = std::min(remaining_size, (
size_t)_meta.fec_oti.encoding_symbol_length);
219 assert(buffer_ptr + symbol_length <= _buffer + _meta.fec_oti.transfer_length);
221 SourceBlock::Symbol symbol{.data = buffer_ptr, .length = symbol_length, .complete =
false};
222 block.symbols[ symbol_id++ ] = symbol;
224 remaining_size -= symbol_length;
225 buffer_ptr += symbol_length;
227 if (remaining_size <= 0)
break;
229 _source_blocks[number++] = block;
235 int nof_symbols = std::ceil((
float)(max_size - 4) / (
float)_meta.fec_oti.encoding_symbol_length);
237 std::vector<EncodingSymbol> symbols;
239 for (
auto& block : _source_blocks) {
240 if (cnt >= nof_symbols)
break;
242 if (!block.second.complete) {
243 for (
auto& symbol : block.second.symbols) {
244 if (cnt >= nof_symbols)
break;
246 if (!symbol.second.complete && !symbol.second.queued) {
247 symbols.emplace_back(symbol.first, block.first, symbol.second.data, symbol.second.length, _meta.fec_oti.encoding_id);
248 symbol.second.queued =
true;
260 for (
auto& symbol : symbols) {
261 auto block = _source_blocks.find(symbol.source_block_number());
262 if (block != _source_blocks.end()) {
263 auto sym = block->second.symbols.find(symbol.id());
264 if (sym != block->second.symbols.end()) {
265 sym->second.queued =
false;
266 sym->second.complete = success;
268 check_source_block_completion(block->second);
269 check_file_completion();
276 if (!_been_encoded && !_meta.content_encoding.empty()) {
277 if (_meta.content_encoding ==
"gzip" || _meta.content_encoding==
"deflate") {
278 auto decomp_buffer = _buffer;
279 bool own_decomp = _own_buffer;
280 std::shared_ptr<unsigned char> comp_buffer(
new unsigned char[16384]);
282 .next_in =
reinterpret_cast<unsigned char*
>(decomp_buffer),
283 .avail_in =
static_cast<uint32_t
>(_meta.content_length),
284 .next_out = comp_buffer.get(),
287 spdlog::debug(
"Compressing contents with {}", _meta.content_encoding);
289 if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
291 auto zstate = deflate(&zs, Z_FINISH);
293 while (zstate == Z_OK) {
294 spdlog::debug(
"Part compressed: {} bytes", 16384-zs.avail_out);
295 _buffer =
reinterpret_cast<char*
>(realloc(_buffer, zs.total_out));
296 memcpy(_buffer+last_out, comp_buffer.get(), 16384-zs.avail_out);
297 last_out = zs.total_out;
299 zs.avail_out = 16384;
300 zs.next_out = comp_buffer.get();
301 zstate = inflate(&zs, Z_FINISH);
303 if (zstate==Z_STREAM_END) {
304 if (last_out != zs.total_out) {
305 spdlog::debug(
"Finish compress, last block is {} bytes. Total {} bytes", 16384-zs.avail_out, zs.total_out);
306 _buffer =
reinterpret_cast<char*
>(realloc(_buffer, zs.total_out));
307 memcpy(_buffer+last_out, comp_buffer.get(), 16384-zs.avail_out);
310 _meta.fec_oti.transfer_length = zs.total_out;
312 spdlog::error(
"Error compressing file {}: {}", _meta.toi, zs.msg);
317 if (own_decomp) free(decomp_buffer);
320 spdlog::error(
"Unknown Content-Encoding {}", _meta.content_encoding);
321 throw "Content-Encoding not known";
324 _been_encoded =
true;
325 _been_decoded =
false;
331 if (!_been_decoded && !_meta.content_encoding.empty()) {
332 if (_meta.content_encoding ==
"gzip" || _meta.content_encoding==
"deflate") {
333 auto comp_buffer = _buffer;
334 bool own_comp = _own_buffer;
335 std::shared_ptr<unsigned char> decomp_buffer(
new unsigned char[16384]);
337 .next_in =
reinterpret_cast<unsigned char*
>(comp_buffer),
338 .avail_in =
static_cast<uint32_t
>(_meta.fec_oti.transfer_length),
339 .next_out = decomp_buffer.get(),
342 spdlog::debug(
"Decompressing contents with {}", _meta.content_encoding);
344 inflateInit2(&zs, 15 | ((_meta.content_encoding ==
"gzip")?16:0));
346 auto zstate = inflate(&zs, Z_FINISH);
348 while (zstate == Z_OK) {
349 spdlog::debug(
"Part decompressed: {} bytes", 16384-zs.avail_out);
350 _buffer =
reinterpret_cast<char*
>(realloc(_buffer, zs.total_out));
351 memcpy(_buffer+last_out, decomp_buffer.get(), 16384-zs.avail_out);
352 last_out = zs.total_out;
354 zs.avail_out = 16384;
355 zs.next_out = decomp_buffer.get();
356 zstate = inflate(&zs, Z_FINISH);
358 if (zstate==Z_STREAM_END) {
359 if (last_out != zs.total_out) {
360 spdlog::debug(
"Finish decompress, last block is {} bytes. Total {} bytes", 16384-zs.avail_out, zs.total_out);
361 _buffer =
reinterpret_cast<char*
>(realloc(_buffer, zs.total_out));
362 memcpy(_buffer+last_out, decomp_buffer.get(), 16384-zs.avail_out);
365 if (!_meta.content_length) {
366 _meta.content_length = zs.total_out;
367 }
else if (_meta.content_length != zs.total_out) {
368 spdlog::error(
"Decompressed length does not match expected Content-Length ({} != {})", _meta.content_length, zs.total_out);
371 spdlog::error(
"Error decompressing file {}: {}", _meta.toi, zs.msg);
375 if (own_comp) free(comp_buffer);
377 spdlog::error(
"Unknown Content-Encoding {}", _meta.content_encoding);
378 throw "Content-Encoding not known";
381 _been_decoded =
true;
382 _been_encoded =
false;
385 if (!_meta.content_md5.empty()) {
386 unsigned char md5[MD5_DIGEST_LENGTH];
387 MD5((
const unsigned char*)buffer(), length(), md5);
389 auto content_md5 = base64_decode(_meta.content_md5);
390 if (memcmp(md5, content_md5.c_str(), MD5_DIGEST_LENGTH) != 0) {
391 spdlog::debug(
"MD5 mismatch for TOI {}, discarding", _meta.toi);
394 for (
auto& block : _source_blocks) {
395 for (
auto& symbol : block.second.symbols) {
396 symbol.second.complete =
false;
398 block.second.complete =
false;
A class for handling FEC encoding symbols.
const FecOti & fec_oti() const
Get the FEC OTI values.
void put_symbol(const EncodingSymbol &symbol)
Write the data from an encoding symbol into the appropriate place in the buffer.
size_t length() const
Get the data buffer length.
virtual ~File()
Default destructor.
void encode()
Encode the buffer using the Content-Encoding.
File(LibFlute::FileDeliveryTable::FileEntry entry)
Create a file from an FDT entry (used for reception)
void mark_completed(const std::vector< EncodingSymbol > &symbols, bool success)
Mark encoding symbols as completed.
std::vector< EncodingSymbol > get_next_symbols(size_t max_size)
Get the next encoding symbols that fit in max_size bytes.
void decode()
Decode the buffer using the Content-Encoding.
An entry for a file in the FDT.
std::string content_location