Skip to content

Commit

Permalink
Corrected reading of string functions and added a stream based IsMdfF…
Browse files Browse the repository at this point in the history
…ile() function. Added function for exporting attachment data to a stream.
  • Loading branch information
ihedvall committed Nov 27, 2024
1 parent 55fa6a9 commit efd54cc
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 12 deletions.
42 changes: 40 additions & 2 deletions include/mdf/mdfreader.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,24 @@ using ChannelObserverPtr = std::unique_ptr<IChannelObserver>;
/** \brief List of observer. */
using ChannelObserverList = std::vector<ChannelObserverPtr>;

/// \brief Returns true if the file is an MDF file.
/** \brief Returns true if the file is an MDF file.
*
* Check if a file is an MDF file. The function reads the first 64
* bytes (ID block) of the file and test if it is an MDF file.
* @param filename Full path to the file.
* @return True if this is a MDF file.
*/
[[nodiscard]] bool IsMdfFile(const std::string& filename);

/** \brief Returns true if the stream buffer is an MDF file.
*
* Check if a stream buffer is an MDF file. The function reads the first 64
* bytes (ID block) of the buffer and test if it is an MDF file.
* @param buffer Reference to the stream buffer.
* @return True if this is a MDF stream.
*/
[[nodiscard]] bool IsMdfFile(std::streambuf& buffer );

/// \brief Creates and attaches a channel sample observer.
[[nodiscard]] ChannelObserverPtr CreateChannelObserver(
const IDataGroup& data_group, const IChannelGroup& group,
Expand Down Expand Up @@ -142,10 +157,33 @@ class MdfReader {
bool ReadMeasurementInfo(); ///< Reads everything but not CG and raw data.
bool ReadEverythingButData(); ///< Reads all blocks but not raw data.

/** \brief Export the attachment data to a destination file. */
/** \brief Export the attachment data to a destination file.
*
* Export an attachment block to a file. If the attachment is an external
* referenced file, that file is copied to the destination file.
* If the attachment is embedded in the MDF file, the attachment data is
* copied to the destination file.
*
* Any existing file will be overwritten.
* @param attachment Reference to the attachment block
* @param dest_file Full path to the
* @return True if the export was successful.
*/
bool ExportAttachmentData(const IAttachment& attachment,
const std::string& dest_file);

/** \brief Export an embedded attachment to a stream buffer.
*
* This function export an attachment data to an external stream buffer.
* Note that this function shouldn't be used with external referenced
* files.
*
* @param attachment Reference to an attachment.
* @param dest_buffer Refrence to an external stream buffer.
* @return True if the export was successful.
*/
bool ExportAttachmentData(const IAttachment& attachment,
std::streambuf& dest_buffer);
/** \brief Reads all sample, sample reduction and signal data into memory.
*
* Reads in all data bytes that belongs to a data group (DG). The function
Expand Down
25 changes: 25 additions & 0 deletions mdflib/src/at4block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,31 @@ void At4Block::ReadData(std::streambuf& buffer,
}
}

void At4Block::ReadData(std::streambuf& buffer,
std::streambuf& dest_buffer) const {
if (data_position_ <= 0) {
throw std::invalid_argument("File is not opened or data position not read");
}
SetFilePosition(buffer, data_position_);
if (IsEmbedded()) {
const bool error = IsCompressed() ? !Inflate(buffer, dest_buffer, nof_bytes_)
: !CopyBytes(buffer, dest_buffer, nof_bytes_);
if ( error ) {
throw std::ios_base::failure("Failed to copy correct number of bytes");
}
} else {
// Need to copy the source file
std::ifstream source(filename_, std::ios_base::in | std::ios_base::binary);
if (!source.is_open()) {
throw std::runtime_error("Failed to open the source file.");
}
for (int input = source.get(); !source.eof(); input = source.get()) {
dest_buffer.sputc(static_cast<char>(input));
}
}
// Difficult to check that the checksum was correct.
}

void At4Block::IsEmbedded(bool embed) {
if (embed) {
flags_ |= At4Flags::kEmbeddedData;
Expand Down
1 change: 1 addition & 0 deletions mdflib/src/at4block.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class At4Block : public MdfBlock, public IAttachment {
uint64_t Read(std::streambuf& buffer) override;

void ReadData(std::streambuf& buffer, const std::string& dest_file) const;
void ReadData(std::streambuf& buffer, std::streambuf& dest_buffer) const;

uint64_t Write(std::streambuf& buffer) override;
[[nodiscard]] std::optional<std::string> Md5() const override;
Expand Down
1 change: 1 addition & 0 deletions mdflib/src/idblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ void IdBlock::GetBlockProperty(BlockPropertyList &dest) const {
uint64_t IdBlock::Read(std::streambuf& buffer) {
block_type_ = "ID";
block_length_ = 64;
SetFirstFilePosition(buffer);

file_position_ = GetFilePosition(buffer);
uint64_t bytes = ReadStr(buffer, file_identifier_, 8);
Expand Down
17 changes: 14 additions & 3 deletions mdflib/src/mdfblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,18 @@ uint64_t WriteBytes(std::streambuf& buffer, uint64_t nof_bytes) {

uint64_t ReadStr(std::streambuf& buffer, std::string &dest, uint64_t size) {
std::ostringstream temp;

for (uint64_t ii = 0; ii < size; ++ii) {
const auto input = buffer.sbumpc();
uint64_t ii;
for (ii = 0; ii < size; ++ii) {
const int input = buffer.sbumpc();
if (input == '\0') {
++ii;
break;
}
temp << static_cast<char>(input);
}
// Correct the file position
if (ii < size) {
StepFilePosition(buffer, size - ii);
}
dest = temp.str();
MdfHelper::Trim(dest);
Expand Down Expand Up @@ -390,6 +398,9 @@ size_t MdfBlock::ReadHeader4(std::FILE *file) {
uint64_t MdfBlock::ReadHeader3(std::streambuf& buffer) {
file_position_ = GetFilePosition(buffer);
uint64_t bytes = ReadStr(buffer, block_type_, 2);
if (block_type_ == "##") {
throw std::runtime_error("MDF 4 header detected. This is not an MDF 3 file.");
}
bytes += ReadNumber(buffer, block_size_);
block_length_ = block_size_;
return bytes;
Expand Down
42 changes: 40 additions & 2 deletions mdflib/src/mdfreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,24 @@ bool IsMdfFile(const std::string &filename) {
if (!file.is_open()) {
return false;
}
const bool is_mdf = IsMdfFile(file);
file.close();
return is_mdf;
}

bool IsMdfFile(std::streambuf &buffer) {
std::filebuf file;

detail::IdBlock oId;
bool bError = true;
try {
uint64_t bytes = oId.Read(file);
uint64_t bytes = oId.Read(buffer);
if (bytes == 64) {
bError = false;
}
} catch (const std::exception &) {
bError = true;
}
file.close();
if (bError) {
return false;
}
Expand Down Expand Up @@ -351,8 +357,11 @@ void MdfReader::Close() {
auto* file_buf = dynamic_cast<std::filebuf*>(buffer);
if (file_buf != nullptr && file_buf->is_open()) {
file_buf->close();
} else {
buffer->pubsync(); // Should flush output
}
} catch (const std::exception& err) {

MDF_ERROR() << "Dynamic cast of pointer failed. Error: " << err.what();
}
}
Expand Down Expand Up @@ -460,6 +469,35 @@ bool MdfReader::ExportAttachmentData(const IAttachment &attachment,
return no_error;
}

bool MdfReader::ExportAttachmentData(const IAttachment &attachment,
std::streambuf &dest_buffer) {
if (!instance_ || !file_) {
MDF_ERROR() << "No instance created. File: " << filename_;
return false;
}

bool shall_close = !IsOpen() && Open();
if (!IsOpen()) {
MDF_ERROR() << "Failed to open file. File: " << filename_;
return false;
}

bool no_error = true;
try {
auto &at4 = dynamic_cast<const detail::At4Block &>(attachment);
at4.ReadData(*file_, dest_buffer);
} catch (const std::exception &error) {
MDF_ERROR() << "Failed to read the file information blocks. Error: "
<< error.what();
no_error = false;
}

if (shall_close) {
Close();
}
return no_error;
}

bool MdfReader::ReadData(IDataGroup &data_group) {
if (!instance_ || !file_) {
MDF_ERROR() << "No instance created. File: " << filename_;
Expand Down
4 changes: 2 additions & 2 deletions mdflib/src/tx3block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Tx3Block::Tx3Block(std::string text) : text_(std::move(text)) {}

uint64_t Tx3Block::Read(std::streambuf& buffer) {
uint64_t bytes = ReadHeader3(buffer);
bytes += ReadStr(buffer, text_, block_size_ - bytes -1);
return bytes + 1;
bytes += ReadStr(buffer, text_, block_length_ - bytes);
return bytes;
}

std::string Tx3Block::Text() const {
Expand Down
4 changes: 2 additions & 2 deletions mdflib/src/tx4block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ size_t Tx4Block::Write(std::FILE *file) {

uint64_t Tx4Block::Read(std::streambuf& buffer) {
uint64_t bytes = ReadHeader4(buffer);
bytes += ReadStr(buffer, text_, block_size_ - bytes - 1);
return bytes + 1;
bytes += ReadStr(buffer, text_, block_length_ - bytes);
return bytes;
}

uint64_t Tx4Block::Write(std::streambuf& buffer) {
Expand Down
1 change: 1 addition & 0 deletions mdflib/src/zlibutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ bool Deflate(const std::string& filename, ByteArray& buf_out) {
reinterpret_cast<char*>(buf_in.data()),
static_cast<std::streamsize>(size) );
file.close();

if (nof_bytes < size) {
buf_in.resize(static_cast<size_t>(nof_bytes), 0);
}
Expand Down
15 changes: 14 additions & 1 deletion mdflib_test/src/testread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,9 @@ TEST_F(TestRead, TestStreamInterface) // NOLINT
for (const auto &[name, filename] : kMdfList) {
uint64_t file_size = 0;
// Read in file into memory

ASSERT_TRUE(IsMdfFile(filename));

try {
const path fullname(filename);
file_size = std::filesystem::file_size(fullname);
Expand All @@ -678,6 +681,7 @@ TEST_F(TestRead, TestStreamInterface) // NOLINT
}
}
in_file.close();
out_buffer.pubsync(); // Doing a flush of buffer to vector
out_buffer.close();

if (file_array.empty()) {
Expand All @@ -688,6 +692,8 @@ TEST_F(TestRead, TestStreamInterface) // NOLINT
std::shared_ptr<std::streambuf> input_buffer =
std::make_shared< stream_buffer<array_source> >(file_array.data(), file_array.size());

ASSERT_TRUE(IsMdfFile(*input_buffer));

MdfReader oRead(input_buffer);
EXPECT_TRUE(oRead.ReadMeasurementInfo()) << name;
const auto *mdf_file = oRead.GetFile();
Expand All @@ -697,11 +703,18 @@ TEST_F(TestRead, TestStreamInterface) // NOLINT
ASSERT_TRUE(file4 != nullptr) << name;
const auto &hd4 = file4->Hd();
const auto &dg_list = hd4.Dg4();
std::cout <<"File: " << name << ", Nof Measurement: " << dg_list.size() << std::endl;
std::cout <<"File: " << name << " (" << file_size / 1'000
<< " kB) , Nof Measurement: " << dg_list.size() << std::endl;
EXPECT_FALSE(dg_list.empty()) << name;
} else {
const auto *file3 = dynamic_cast<const Mdf3File *>(mdf_file);
EXPECT_TRUE(file3 != nullptr);
ASSERT_TRUE(file3 != nullptr) << name;
const auto &hd3 = file3->Hd();
const auto &dg_list = hd3.Dg3();
std::cout <<"File: " << name << " (" << file_size / 1'000
<< " kB) , Nof Measurement: " << dg_list.size() << std::endl;
EXPECT_FALSE(dg_list.empty()) << name;
}
}
}
Expand Down

0 comments on commit efd54cc

Please sign in to comment.