libflute
Classes | Public Member Functions | List of all members
LibFlute::File Class Reference

Represents a file being transmitted or received. More...

#include <File.h>

Public Member Functions

 File (LibFlute::FileDeliveryTable::FileEntry entry)
 Create a file from an FDT entry (used for reception) More...
 
 File (const std::shared_ptr< Transmitter::FileDescription > &file_description)
 Create a file from a Transmitter::FileDescription (used for transmission) More...
 
 File (uint32_t toi, FecOti fec_oti, std::string content_location, std::string content_type, uint64_t expires, char *data, size_t length, bool copy_data=false)
 Create a file from the given parameters (used for transmission) More...
 
virtual ~File ()
 Default destructor. More...
 
void put_symbol (const EncodingSymbol &symbol)
 Write the data from an encoding symbol into the appropriate place in the buffer. More...
 
bool complete () const
 Check if the file is complete. More...
 
char * buffer () const
 Get the data buffer. More...
 
size_t length () const
 Get the data buffer length. More...
 
void encode ()
 Encode the buffer using the Content-Encoding. More...
 
void decode ()
 Decode the buffer using the Content-Encoding. More...
 
bool is_encoded () const
 Check if the buffer is content encoded. More...
 
const FecOtifec_oti () const
 Get the FEC OTI values. More...
 
const LibFlute::FileDeliveryTable::FileEntrymeta () const
 Get the file metadata from its FDT entry. More...
 
unsigned long received_at () const
 Timestamp of file reception. More...
 
void log_access ()
 Log access to the file by incrementing a counter. More...
 
unsigned access_count () const
 Get the access counter value. More...
 
std::vector< EncodingSymbolget_next_symbols (size_t max_size)
 Get the next encoding symbols that fit in max_size bytes. More...
 
void mark_completed (const std::vector< EncodingSymbol > &symbols, bool success)
 Mark encoding symbols as completed. More...
 
void set_fdt_instance_id (uint16_t id)
 Set the FDT instance ID. More...
 
uint16_t fdt_instance_id ()
 Get the FDT instance ID. More...
 

Detailed Description

Represents a file being transmitted or received.

Definition at line 30 of file File.h.

Constructor & Destructor Documentation

◆ File() [1/3]

LibFlute::File::File ( LibFlute::FileDeliveryTable::FileEntry  entry)

Create a file from an FDT entry (used for reception)

Parameters
entryFDT entry

Definition at line 38 of file File.cpp.

39  : _meta( std::move(entry) )
40  , _received_at( time(nullptr) )
41  , _file_description()
42 {
43  spdlog::debug("Creating File from FileEntry");
44  // Allocate a data buffer
45  spdlog::debug("Allocating buffer");
46  _buffer = (char*)malloc(_meta.fec_oti.transfer_length);
47  if (_buffer == nullptr)
48  {
49  throw std::runtime_error("Failed to allocate file buffer");
50  }
51  _own_buffer = true;
52 
53  this->calculate_partitioning();
54  this->create_blocks();
55 }
uint64_t transfer_length
Definition: flute_types.h:54

◆ File() [2/3]

LibFlute::File::File ( const std::shared_ptr< Transmitter::FileDescription > &  file_description)

Create a file from a Transmitter::FileDescription (used for transmission)

Parameters
file_descriptionTransmitter File Description

Definition at line 57 of file File.cpp.

58  : _meta()
59  , _file_description(file_description)
60 {
61  spdlog::debug("Creating File from FileDescription");
62 
63  auto length = _file_description->data_length();
64  _buffer = (char*)malloc(length);
65  if (_buffer == nullptr)
66  {
67  throw std::runtime_error("No data allocated");
68  }
69  _own_buffer = true;
70  memcpy(_buffer, _file_description->data(), length);
71  _meta = _file_description->file_entry();
72 
73  // for no-code
76  } else {
77  throw std::runtime_error("Unsupported FEC scheme");
78  }
79 
80  encode();
81 
82  calculate_partitioning();
83  create_blocks();
84 }
size_t length() const
Get the data buffer length.
Definition: File.h:90
void encode()
Encode the buffer using the Content-Encoding.
Definition: File.cpp:285
FecScheme encoding_id
Definition: flute_types.h:52

◆ File() [3/3]

LibFlute::File::File ( uint32_t  toi,
FecOti  fec_oti,
std::string  content_location,
std::string  content_type,
uint64_t  expires,
char *  data,
size_t  length,
bool  copy_data = false 
)

Create a file from the given parameters (used for transmission)

Parameters
toiTOI of the file
content_locationContent location URI to use
content_typeMIME type
expiresExpiry value (in seconds since the NTP epoch)
dataPointer to the data buffer
lengthLength of the buffer
copy_dataCopy the buffer. If false (the default), the caller must ensure the buffer remains valid while the file is being transmitted.

Definition at line 86 of file File.cpp.

94  : _own_buffer(false)
95  , _meta()
96  , _file_description()
97 {
98  spdlog::debug("Creating File from data");
99  if (copy_data) {
100  spdlog::debug("Allocating buffer");
101  _buffer = (char*)malloc(length);
102  if (_buffer == nullptr)
103  {
104  throw std::runtime_error("Failed to allocate file buffer");
105  }
106  memcpy(_buffer, data, length);
107  _own_buffer = true;
108  } else {
109  _buffer = data;
110  }
111 
112  unsigned char md5[MD5_DIGEST_LENGTH];
113  MD5((const unsigned char*)data, length, md5);
114 
115  _meta.toi = toi;
116  _meta.content_location = std::move(content_location);
117  _meta.content_type = std::move(content_type);
118  _meta.content_length = length;
119  _meta.content_md5 = base64_encode(md5, MD5_DIGEST_LENGTH);
120  _meta.expires = expires;
121  _meta.fec_oti = fec_oti;
122 
123  // for no-code
126  } else {
127  throw std::runtime_error("Unsupported FEC scheme");
128  }
129 
130  this->calculate_partitioning();
131  this->create_blocks();
132 }
const FecOti & fec_oti() const
Get the FEC OTI values.
Definition: File.h:112

◆ ~File()

LibFlute::File::~File ( )
virtual

Default destructor.

Definition at line 134 of file File.cpp.

135 {
136  spdlog::debug("Destroying File");
137  if (_own_buffer && _buffer != nullptr)
138  {
139  spdlog::debug("Freeing buffer");
140  free(_buffer);
141  }
142 }

Member Function Documentation

◆ access_count()

unsigned LibFlute::File::access_count ( ) const
inline

Get the access counter value.

Definition at line 132 of file File.h.

132 { return _access_count; };

◆ buffer()

char* LibFlute::File::buffer ( ) const
inline

Get the data buffer.

Definition at line 85 of file File.h.

85 { return _buffer; };

◆ complete()

bool LibFlute::File::complete ( ) const
inline

Check if the file is complete.

Definition at line 80 of file File.h.

80 { return _complete; };

◆ decode()

auto LibFlute::File::decode ( )

Decode the buffer using the Content-Encoding.

Will check the MD5 sum after decoding, if present.

Definition at line 340 of file File.cpp.

341 {
342  if (!_been_decoded && !_meta.content_encoding.empty()) {
343  if (_meta.content_encoding == "gzip" || _meta.content_encoding=="deflate") {
344  auto comp_buffer = _buffer;
345  bool own_comp = _own_buffer;
346  std::shared_ptr<unsigned char[]> decomp_buffer(new unsigned char[16384]);
347  z_stream zs = {
348  .next_in = reinterpret_cast<unsigned char*>(comp_buffer),
349  .avail_in = static_cast<uint32_t>(_meta.fec_oti.transfer_length),
350  .next_out = decomp_buffer.get(),
351  .avail_out = 16384
352  };
353  spdlog::debug("Decompressing contents with {}", _meta.content_encoding);
354 
355  inflateInit2(&zs, 15 | ((_meta.content_encoding == "gzip")?16:0));
356  _buffer = nullptr;
357  auto zstate = inflate(&zs, Z_FINISH);
358  size_t last_out = 0;
359  while (zstate == Z_OK) {
360  spdlog::debug("Part decompressed: {} bytes", 16384-zs.avail_out);
361  _buffer = reinterpret_cast<char*>(realloc(_buffer, zs.total_out));
362  memcpy(_buffer+last_out, decomp_buffer.get(), 16384-zs.avail_out);
363  last_out = zs.total_out;
364  _own_buffer = true;
365  zs.avail_out = 16384;
366  zs.next_out = decomp_buffer.get();
367  zstate = inflate(&zs, Z_FINISH);
368  }
369  if (zstate==Z_STREAM_END) {
370  if (last_out != zs.total_out) {
371  spdlog::debug("Finish decompress, last block is {} bytes. Total {} bytes", 16384-zs.avail_out, zs.total_out);
372  _buffer = reinterpret_cast<char*>(realloc(_buffer, zs.total_out));
373  memcpy(_buffer+last_out, decomp_buffer.get(), 16384-zs.avail_out);
374  _own_buffer = true;
375  }
376  if (!_meta.content_length) {
377  _meta.content_length = zs.total_out;
378  } else if (_meta.content_length != zs.total_out) {
379  spdlog::error("Decompressed length does not match expected Content-Length ({} != {})", _meta.content_length, zs.total_out);
380  }
381  } else {
382  spdlog::error("Error decompressing file {}: {}", _meta.toi, zs.msg);
383  throw zs.msg;
384  }
385 
386  if (own_comp) free(comp_buffer);
387  } else {
388  spdlog::error("Unknown Content-Encoding {}", _meta.content_encoding);
389  throw std::runtime_error("Content-Encoding not known");
390  }
391 
392  _been_decoded = true;
393  _been_encoded = false;
394 
395  // Check MD5
396  if (!_meta.content_md5.empty()) {
397  unsigned char md5[MD5_DIGEST_LENGTH];
398  MD5((const unsigned char*)buffer(), length(), md5);
399 
400  auto content_md5 = base64_decode(_meta.content_md5);
401  if (memcmp(md5, content_md5.c_str(), MD5_DIGEST_LENGTH) != 0) {
402  spdlog::debug("MD5 mismatch for TOI {}, discarding", _meta.toi);
403 
404  // MD5 mismatch, try again
405  for (auto& block : _source_blocks) {
406  for (auto& symbol : block.second.symbols) {
407  symbol.second.complete = false;
408  }
409  block.second.complete = false;
410  }
411  _complete = false;
412  }
413  }
414  }
415 }
char * buffer() const
Get the data buffer.
Definition: File.h:85

◆ encode()

auto LibFlute::File::encode ( )

Encode the buffer using the Content-Encoding.

Definition at line 285 of file File.cpp.

286 {
287  if (!_been_encoded && !_meta.content_encoding.empty()) {
288  if (_meta.content_encoding == "gzip" || _meta.content_encoding=="deflate") {
289  auto decomp_buffer = _buffer;
290  bool own_decomp = _own_buffer;
291  std::shared_ptr<unsigned char[]> comp_buffer(new unsigned char[16384]);
292  z_stream zs = {
293  .next_in = reinterpret_cast<unsigned char*>(decomp_buffer),
294  .avail_in = static_cast<uint32_t>(_meta.content_length),
295  .next_out = comp_buffer.get(),
296  .avail_out = 16384
297  };
298  spdlog::debug("Compressing contents with {}", _meta.content_encoding);
299 
300  if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
301  _buffer = nullptr;
302  auto zstate = deflate(&zs, Z_FINISH);
303  size_t last_out = 0;
304  while (zstate == Z_OK) {
305  spdlog::debug("Part compressed: {} bytes", 16384-zs.avail_out);
306  _buffer = reinterpret_cast<char*>(realloc(_buffer, zs.total_out));
307  memcpy(_buffer+last_out, comp_buffer.get(), 16384-zs.avail_out);
308  last_out = zs.total_out;
309  _own_buffer = true;
310  zs.avail_out = 16384;
311  zs.next_out = comp_buffer.get();
312  zstate = deflate(&zs, Z_FINISH);
313  }
314  if (zstate==Z_STREAM_END) {
315  if (last_out != zs.total_out) {
316  spdlog::debug("Finish compress, last block is {} bytes. Total {} bytes", 16384-zs.avail_out, zs.total_out);
317  _buffer = reinterpret_cast<char*>(realloc(_buffer, zs.total_out));
318  memcpy(_buffer+last_out, comp_buffer.get(), 16384-zs.avail_out);
319  _own_buffer = true;
320  }
321  _meta.fec_oti.transfer_length = zs.total_out;
322  } else {
323  spdlog::error("Error compressing file {}: {}", _meta.toi, zs.msg);
324  throw zs.msg;
325  }
326  deflateEnd(&zs);
327 
328  if (own_decomp) free(decomp_buffer);
329  }
330  } else {
331  spdlog::error("Unknown Content-Encoding {}", _meta.content_encoding);
332  throw std::runtime_error("Content-Encoding not known");
333  }
334 
335  _been_encoded = true;
336  _been_decoded = false;
337  }
338 }

◆ fdt_instance_id()

uint16_t LibFlute::File::fdt_instance_id ( )
inline

Get the FDT instance ID.

Definition at line 152 of file File.h.

152 { return _fdt_instance_id; };

◆ fec_oti()

const FecOti& LibFlute::File::fec_oti ( ) const
inline

Get the FEC OTI values.

Definition at line 112 of file File.h.

112 { return _meta.fec_oti; };

◆ get_next_symbols()

auto LibFlute::File::get_next_symbols ( size_t  max_size)

Get the next encoding symbols that fit in max_size bytes.

Definition at line 244 of file File.cpp.

245 {
246  int nof_symbols = std::ceil((float)(max_size - 4) / (float)_meta.fec_oti.encoding_symbol_length);
247  auto cnt = 0;
248  std::vector<EncodingSymbol> symbols;
249 
250  for (auto& block : _source_blocks) {
251  if (cnt >= nof_symbols) break;
252 
253  if (!block.second.complete) {
254  for (auto& symbol : block.second.symbols) {
255  if (cnt >= nof_symbols) break;
256 
257  if (!symbol.second.complete && !symbol.second.queued) {
258  symbols.emplace_back(symbol.first, block.first, symbol.second.data, symbol.second.length, _meta.fec_oti.encoding_id);
259  symbol.second.queued = true;
260  cnt++;
261  }
262  }
263  }
264  }
265  return symbols;
266 
267 }
uint32_t encoding_symbol_length
Definition: flute_types.h:55

◆ is_encoded()

bool LibFlute::File::is_encoded ( ) const
inline

Check if the buffer is content encoded.

Definition at line 107 of file File.h.

107 { return _been_encoded || !_been_decoded; };

◆ length()

size_t LibFlute::File::length ( ) const
inline

Get the data buffer length.

Definition at line 90 of file File.h.

90 { return _been_decoded?_meta.content_length:_meta.fec_oti.transfer_length; };

◆ log_access()

void LibFlute::File::log_access ( )
inline

Log access to the file by incrementing a counter.

Definition at line 127 of file File.h.

127 { _access_count++; };

◆ mark_completed()

auto LibFlute::File::mark_completed ( const std::vector< EncodingSymbol > &  symbols,
bool  success 
)

Mark encoding symbols as completed.

Definition at line 269 of file File.cpp.

270 {
271  for (auto& symbol : symbols) {
272  auto block = _source_blocks.find(symbol.source_block_number());
273  if (block != _source_blocks.end()) {
274  auto sym = block->second.symbols.find(symbol.id());
275  if (sym != block->second.symbols.end()) {
276  sym->second.queued = false;
277  sym->second.complete = success;
278  }
279  check_source_block_completion(block->second);
280  check_file_completion();
281  }
282  }
283 }

◆ meta()

const LibFlute::FileDeliveryTable::FileEntry& LibFlute::File::meta ( ) const
inline

Get the file metadata from its FDT entry.

Definition at line 117 of file File.h.

117 { return _meta; };

◆ put_symbol()

auto LibFlute::File::put_symbol ( const EncodingSymbol symbol)

Write the data from an encoding symbol into the appropriate place in the buffer.

Definition at line 144 of file File.cpp.

145 {
146  // Bounds must be ">=", not ">": a valid source-block number is
147  // 0.._source_blocks.size()-1, so SBN == size() is already out of range and
148  // indexing with it below would be undefined behaviour. Throw a std::exception
149  // (not a bare const char*, which escapes the receiver's catch(std::exception&)
150  // and terminates the process) so an out-of-range symbol -- e.g. a content
151  // object whose on-air symbol layout exceeds its FDT-declared FEC-OTI -- is
152  // dropped and logged by the caller instead of crashing the client.
153  if (symbol.source_block_number() >= _source_blocks.size()) {
154  throw std::runtime_error(fmt::format("FLUTE: source block number {} out of range (have {} blocks)",
155  symbol.source_block_number(), _source_blocks.size()));
156  }
157 
158  SourceBlock& source_block = _source_blocks[ symbol.source_block_number() ];
159 
160  if (symbol.id() >= source_block.symbols.size()) {
161  throw std::runtime_error(fmt::format("FLUTE: encoding symbol id {} out of range (block {} has {} symbols)",
162  symbol.id(), symbol.source_block_number(), source_block.symbols.size()));
163  }
164 
165  SourceBlock::Symbol& target_symbol = source_block.symbols[symbol.id()];
166 
167  if (!target_symbol.complete) {
168  symbol.decode_to(target_symbol.data, target_symbol.length);
169  target_symbol.complete = true;
170 
171  check_source_block_completion(source_block);
172  check_file_completion();
173  }
174 
175 }

◆ received_at()

unsigned long LibFlute::File::received_at ( ) const
inline

Timestamp of file reception.

Definition at line 122 of file File.h.

122 { return _received_at; };

◆ set_fdt_instance_id()

void LibFlute::File::set_fdt_instance_id ( uint16_t  id)
inline

Set the FDT instance ID.

Definition at line 147 of file File.h.

147 { _fdt_instance_id = id; };

The documentation for this class was generated from the following files: