C/C++ library to decompress tar.gz file while downloading - compression

I download a tar.gz file with libcurl, and need to decompress the file while downloading, that is, when chunk of the file is downloaded, decompress chunk of the file imediately, instead of decompressing the whole file when the whole file is downloaded. Is there any C/C++ libraries that meet my requirements
I tried to use libarchive to extract the file, but it returned truncated gzip input when extracting the first chunk of the file. It seems that libarchive need the whole file to extract it. Here is my code. I am not sure if i used libarchive correctly as I am new to it.
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <atomic>
#include <thread>
// libarchive
#include <archive.h>
#include <archive_entry.h>
#include <curl/curl.h>
struct mydata {
void *buffer;
ssize_t *size;
};
struct curldata {
void *buffer;
ssize_t *size;
CURL *curl;
};
std::atomic<bool> rd(true);
struct archive *archive, *archivefd;
std::atomic<bool> start_read(false);
la_ssize_t libarchiveRead(struct archive* a, void* client_data, const void** block)
{
if(!rd) {
mydata *my_data = (mydata*)client_data;
std::cout << "calling custom read(), size " << *(my_data->size) << std::endl;
*block = my_data->buffer;
rd=true;
return *(my_data->size);
}
return 0;
}
int libarchiveClose(struct archive* a, void* client_data)
{
std::cout << "calling custom close() for archive" << std::endl;
mydata *my_data = (mydata*)client_data;
delete my_data;
return (ARCHIVE_OK);
}
int libarchiveClosefd(struct archive* a, void* client_data)
{
std::cout << "calling custom close() for archivefd" << std::endl;
mydata *my_data = (mydata*)client_data;
delete my_data;
return (ARCHIVE_OK);
}
static size_t curlWriteFunction(void *ptr, size_t size, size_t nmemb, void *write_data) {
//size is always 1
curldata *my_data = (curldata*)(write_data);
*(my_data->size) = nmemb * size;
std::cout << "calling curlWriteFunction(), size: " << size << " , nmemb: " << nmemb
<< " , my_data->size: " << *(my_data->size) << std::endl;
memcpy(my_data->buffer, ptr, *(my_data->size));
curl_easy_pause(my_data->curl, CURL_WRITEFUNC_PAUSE);
rd=false;
return (*(my_data->size));
}
static size_t progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) {
CURL *curl = (CURL *)clientp;
(void)ultotal;
(void)ulnow;
if(dltotal == 0) {
return 0;
}
if(rd) {
curl_easy_pause(curl, CURLPAUSE_CONT);
std::cout << "progress: " << dlnow/dltotal * 100 << "%" << std::endl;
}
return 0;
}
void readarchive(void *client_data) {
struct archive_entry *entry;
int flags = ARCHIVE_EXTRACT_TIME;
flags |= ARCHIVE_EXTRACT_PERM;
flags |= ARCHIVE_EXTRACT_ACL;
flags |= ARCHIVE_EXTRACT_FFLAGS;
while(rd);
std::cout << "calling archive_read_open for archive.." << std::endl;
int res = archive_read_open(archive,
client_data,
nullptr,
(archive_read_callback*)libarchiveRead,
(archive_close_callback*)libarchiveClose);
std::cout << "called archive_read_open for archive.." << std::endl;
res = archive_read_next_header(archive, &(entry));
while(res == ARCHIVE_OK ) {
std::cout << "Extracting for archive " << archive_entry_pathname(entry) << "..." << std::endl;
// extract current entry
archive_read_extract(archive, entry, flags);
// read next if available
res = archive_read_next_header(archive, &(entry));
}
std::cout << "archive_read_next_header for archive failed, errcode: " << res << " error: " << archive_error_string(archive) << std::endl;
}
//size_t curlWriteFunction(void *ptr, size_t size, size_t nmemb,FILE* fptr) {
// //size is always 1
// std::cout << "calling curlWriteFunction().." << std::endl;
// return fwrite(ptr, size, nmemb, fptr);
//}
int main(int argc, char** argv) {
if(argc < 3)
{
std::cout << argv[0] << "{-r | -w} file[s]" << std::endl;
return 1;
}
std::vector<std::string> filenames;
filenames.reserve(argc);
while (*++argv != nullptr)
{
filenames.emplace_back(*argv);
}
bool modeRead = (filenames[0] == "-r");
std::cout << filenames[0] << " " << filenames[1] << std::endl;
// archive related variables
char buff_archive[16 * 1024], buff_archivefd[16 * 1024];
if(modeRead)
{
archive = archive_read_new();
archive_read_support_filter_gzip(archive);
archive_read_support_format_tar(archive);
mydata *client_data = new mydata();
int res;
char *buff1 = new char[16 * 1024];
client_data->size = new ssize_t;
*(client_data->size) = 0;
client_data->buffer = buff1;
curldata *curl_data = new curldata();
curl_data->size=client_data->size;
curl_data->buffer=buff1;
CURL *curl = curl_easy_init();
curl_data->curl = curl;
curl_easy_setopt(curl, CURLOPT_URL, filenames[1].c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, curl);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,progress);
std::thread t(readarchive, client_data);
CURLcode result = curl_easy_perform(curl);
if(result != CURLE_OK) {
std::cout << "curl perform failed, errcode; " << result << " err: " << curl_easy_strerror(result) << std::endl;
}
//std::cout << "calling archive_read_open for archivefd.." << std::endl;
//res = archive_read_open(archivefd,
// client_datafd,
// nullptr,
// (archive_read_callback*)libarchiveReadfd,
// (archive_close_callback*)libarchiveClosefd);
//std::cout << "called archive_read_open for archivefd.." << std::endl;
//res = archive_read_next_header(archivefd, &(entry));
//if (res != ARCHIVE_OK) {
// std::cout << "archive_read_next_header for archivefd failed, errcode: " << res << " error: " << archive_error_string(archivefd) << std::endl;
//}
//while(res == ARCHIVE_OK) {
// std::cout << "Extracting for archivefd " << archive_entry_pathname(entry) << "..." << std::endl;
// // extract current entry
// archive_read_extract(archivefd, entry, flags);
// // read next if available
// res = archive_read_next_header(archivefd, &(entry));
//}
t.join();
delete client_data->size;
delete []buff1;
archive_read_close(archive);
archive_read_free(archive);
archive_read_free(archive);
curl_easy_cleanup(curl);
}
return 0;
}

Related

Double free or corruption C++ Avro

I need to serialize a JSON string using Avro in C++.
I installed the libserdes library (https://github.com/confluentinc/libserdes) and I used the example code
./examples/kafka-serdes-avro-console-producer.cpp
to write a main,
which serializes a JSON string as follows.
{"s":"BTCUSDT","c":"spread","u":123456789,"a":20000.4,"b":21000.5,"A":1.25,"B":0.58,"p":-1,"P":-1,"st":-1,"rt":1675000000123000}
Here is the code
#include "fh_price_t.h"
#include "avro/Encoder.hh"
#include "avro/Decoder.hh"
#include <avro/Generic.hh>
#include <avro/Specific.hh>
#include <avro/Exception.hh>
#include "libserdes/serdescpp-avro.h"
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#define FATAL(reason...) do { \
std::cerr << "% FATAL: " << reason << std::endl; \
exit(1); \
} while (0)
template<class T> std::string to_string_a (const T& x) {
std::ostringstream oss;
oss << x;
return oss.str();
}
/**
* Convert JSON to Avro Datum.
*
* Returns 0 on success or -1 on failure.
*/
int json2avro (Serdes::Schema *schema, const std::string &json,
avro::GenericDatum **datump) {
avro::ValidSchema *avro_schema = schema->object();
/* Input stream from json string */
std::istringstream iss(json);
auto json_is = avro::istreamInputStream(iss);
/* JSON decoder */
avro::DecoderPtr json_decoder = avro::jsonDecoder(*avro_schema);
avro::GenericDatum *datum = new avro::GenericDatum(*avro_schema);
try {
/* Decode JSON to Avro datum */
json_decoder->init(*json_is);
avro::decode(*json_decoder, *datum);
} catch (const avro::Exception &e) {
std::cerr << "% JSON to Avro transformation failed: "
<< e.what() << std::endl;
return -1;
}
*datump = datum;
}
int main (int argc, char* argv) {
izycoinstestavro::fh_price_t price{};
price.s = "BTCUSDT";
price.c = "spread";
price.u = 123456789;
price.a = 20000.4;
price.A = 1.25;
price.b = 21000.52;
price.B = 0.58;
price.p = -1;
price.P = -1;
price.st = -1;
price.rt = 1675000000123000;
std::ifstream file_ifstream("../src/avro/fh_price_t.json");
std::stringstream buffer;
buffer << file_ifstream.rdbuf();
std::string schema_json = buffer.str();
Serdes::Conf *sconf = Serdes::Conf::create();
Serdes::Schema *schema;
std::string errstr;
if (sconf->set("schema.registry.url", "http://localhost:8081", errstr))
FATAL("Conf failed: " << errstr);
if (sconf->set("serializer.framing", "cp1", errstr))
FATAL("Conf failed: " << errstr);
Serdes::Avro *serdes = Serdes::Avro::create(sconf, errstr);
if (!serdes)
FATAL("Failed to create Serdes handle: " << errstr);
schema = Serdes::Schema::add(serdes, "fh_price_t", schema_json, errstr);
if (!schema)
FATAL("Failed to register schema " << "fh_price_t" << ": " << errstr);
std::cout << "% Registered schema " << schema->name() << " with id " << schema->id() << std::endl;
avro::GenericDatum *datum = NULL;
std::vector<char> out;
/* Convert JSON to Avro object */
std::string line = to_string_a(price);
json2avro(schema, line, &datum);
//std::cout << to_string_a(price) << std::endl;
/*if (rr == -1) {
FATAL("Failed to convert JSON to Avro " << to_string_a(price) << ": " << errstr);
}
// Serialize Avro
if (serdes->serialize(schema, datum, out, errstr) == -1) {
std::cerr << "% Avro serialization failed: " << errstr << std::endl;
delete datum;
exit(1);
}
delete datum;
std::cout << "Data Size : " << out.size() << std::endl;*/
return 0;
}
When I run it, I get a double free or corruption (out) error.
The error occurs at the output of the function.
I have located the line that causes the error (because when I remove it, the error disappears)
avro::GenericDatum *datum = new avro::GenericDatum(*avro_schema);
I would like to know why and how to solve it.

BIO_write() is not writing to the file when using BIO_f_base64()

I am trying to learn OPENSSL and following this tutorial. I have written following code to read from a file and write the encoded form in another file.
#include <iostream>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#define MAX_BUFFER_SIZE 512
int main (int argc, char* argv[])
{
BIO* bio_out = nullptr;
BIO* bio_in = nullptr;
BIO* bio_b64 = nullptr;
int in_byte, out_byte;
char buffer[MAX_BUFFER_SIZE];
std::memset(buffer, '\0', MAX_BUFFER_SIZE);
if (argc != 3)
{
std::cout << "Usage: bio_b64_encode <source-read> <encoded-write>\n";
return 1;
}
bio_in = BIO_new_file(argv[1], "r");
bio_out = BIO_new_file(argv[2], "wb");
bio_b64 = BIO_new(BIO_f_base64());
BIO_push(bio_b64, bio_out);
in_byte = BIO_read(bio_in, buffer, MAX_BUFFER_SIZE);
while (in_byte > 0)
{
std::cout << "Read " << in_byte << " bytes.\n";
out_byte = BIO_write(bio_b64, buffer, in_byte);
std::cout << "Wrote " << out_byte << " bytes.\n";
if (in_byte != out_byte)
{
std::cout << "In bytes: " << in_byte << "and Out bytes: " << out_byte << " are not equal.\n";
BIO_free(bio_in);
BIO_free(bio_out);
return 1;
}
in_byte = BIO_read(bio_in, buffer, MAX_BUFFER_SIZE);
}
BIO_free(bio_in);
BIO_free_all(bio_b64);
return 0;
}
The code reads properly from the input file given in argv[1] and tries to write to argv[2] but when I open the output file, it is empty but it should have the encoded text from the input file.
the output of the above program when I run ./bio_b64_encode in.txt out.txt is
Read 22 bytes.
Wrote 22 bytes.
but the output file is empty. If I remove the encoding constraints and try to simply write whatever I read from input file (the code is below), it works properly.
#include <iostream>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#define MAX_BUFFER_SIZE 512
int main (int argc, char* argv[])
{
BIO* bio_out = nullptr;
BIO* bio_in = nullptr;
BIO* bio_b64 = nullptr;
int in_byte, out_byte;
char buffer[MAX_BUFFER_SIZE];
std::memset(buffer, '\0', MAX_BUFFER_SIZE);
if (argc != 3)
{
std::cout << "Usage: bio_write <file-read> <file-write>\n";
return 1;
}
bio_in = BIO_new_file(argv[1], "r");
bio_out = BIO_new_file(argv[2], "w");
in_byte = BIO_read(bio_in, buffer, MAX_BUFFER_SIZE);
while (in_byte > 0)
{
std::cout << "Read " << in_byte << " bytes.\n";
out_byte = BIO_write(bio_out, buffer, in_byte);
std::cout << "Wrote " << out_byte << " bytes.\n";
if (in_byte != out_byte)
{
std::cout << "In bytes: " << in_byte << "and Out bytes: " << out_byte << " are not equal.\n";
BIO_free(bio_in);
BIO_free(bio_out);
return 1;
}
in_byte = BIO_read(bio_in, buffer, MAX_BUFFER_SIZE);
}
BIO_free(bio_in);
BIO_free_all(bio_out);
return 0;
}
I am not able to figure out what am I missing here.

Streams API For Fast-RTPS

I want to use fast-rtps to publish video(streams data) to subscriber. While I publish ten consecutive jpg file successfully, every picture received by subscriber wastes a lot of time to processing because I use function get_byte_value get a pixel one by one.
Do anyone know how to publish and subscribe more efficiently by fast-rtps midleware? (Create a new type? other?)
Below is my publisher's and subscriber's code:
Publisher.cpp
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// Hpshboss modifys code from eprosima's github example;
// Licensed under the Apache License, Version 2.0 (the "License");
/**
* #file PicturePublisher.cpp
*
*/
#include "Publisher.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/PublisherAttributes.h>
#include <fastrtps/publisher/Publisher.h>
#include <fastrtps/Domain.h>
#include <fastrtps/types/DynamicTypeBuilderFactory.h>
#include <fastrtps/types/DynamicDataFactory.h>
#include <fastrtps/types/DynamicTypeBuilder.h>
#include <fastrtps/types/DynamicTypeBuilderPtr.h>
#include <fastrtps/types/DynamicType.h>
#include <thread>
#include <time.h>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;
using namespace eprosima::fastrtps::types;
// using namespace cv;
PicturePublisher::PicturePublisher()
: mp_participant(nullptr)
, mp_publisher(nullptr)
, m_DynType(DynamicType_ptr(nullptr))
{
}
bool PicturePublisher::init()
{
cv::Mat image = cv::imread("drone.jpg", 1);
std::vector<unsigned char> buffer;
cv::imencode(".jpg", image, buffer);
// Create basic builders
DynamicTypeBuilder_ptr struct_type_builder(DynamicTypeBuilderFactory::get_instance()->create_struct_builder());
DynamicType_ptr octet_type(DynamicTypeBuilderFactory::get_instance()->create_byte_type());
DynamicTypeBuilder_ptr sequence_type_builder(DynamicTypeBuilderFactory::get_instance()->create_sequence_builder(octet_type, 3873715));
DynamicType_ptr sequence_type = sequence_type_builder->build();
// Add members to the struct. By the way, id must be consecutive starting by zero.
struct_type_builder->add_member(0, "index", DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(1, "size", DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(2, "Picture", sequence_type);
struct_type_builder->set_name("Picture"); // Need to be same with topic data type
DynamicType_ptr dynType = struct_type_builder->build();
m_DynType.SetDynamicType(dynType);
m_DynHello = DynamicDataFactory::get_instance()->create_data(dynType);
m_DynHello->set_uint32_value(0, 0);
m_DynHello->set_uint32_value(buffer.size(), 1);
MemberId id;
// std::cout << "init: " << id << std::endl;
DynamicData* sequence_data = m_DynHello->loan_value(2);
for (int i = 0; i < buffer.size(); i++) {
if (i == buffer.size() - 1) {
std::cout << "Total Size: " << i + 1 << std::endl;
}
sequence_data->insert_byte_value(buffer[i], id);
}
m_DynHello->return_loaned_value(sequence_data);
ParticipantAttributes PParam;
PParam.rtps.setName("DynPicture_pub");
mp_participant = Domain::createParticipant(PParam, (ParticipantListener*)&m_part_list);
if (mp_participant == nullptr)
{
return false;
}
//REGISTER THE TYPE
Domain::registerDynamicType(mp_participant, &m_DynType);
//CREATE THE PUBLISHER
PublisherAttributes Wparam;
Wparam.topic.topicKind = NO_KEY;
Wparam.topic.topicDataType = "Picture";
Wparam.topic.topicName = "PictureTopic";
mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);
if (mp_publisher == nullptr)
{
return false;
}
return true;
}
PicturePublisher::~PicturePublisher()
{
Domain::removeParticipant(mp_participant);
DynamicDataFactory::get_instance()->delete_data(m_DynHello);
Domain::stopAll();
}
void PicturePublisher::PubListener::onPublicationMatched(
Publisher* /*pub*/,
MatchingInfo& info)
{
if (info.status == MATCHED_MATCHING)
{
n_matched++;
firstConnected = true;
std::cout << "Publisher matched" << std::endl;
}
else
{
n_matched--;
std::cout << "Publisher unmatched" << std::endl;
}
}
void PicturePublisher::PartListener::onParticipantDiscovery(
Participant*,
ParticipantDiscoveryInfo&& info)
{
if (info.status == ParticipantDiscoveryInfo::DISCOVERED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " discovered" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::REMOVED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " removed" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::DROPPED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " dropped" << std::endl;
}
}
void PicturePublisher::runThread(
uint32_t samples,
uint32_t sleep)
{
uint32_t i = 0;
while (!stop && (i < samples || samples == 0))
{
if (publish(samples != 0))
{
uint32_t index;
m_DynHello->get_uint32_value(index, 0);
std::cout << "runThreading...; \tSample Index: " << index << "; \t";
uint32_t size;
m_DynHello->get_uint32_value(size, 1);
std::cout << "size: " << size << std::endl;
if (i == 9){
std::cout << "Structure message" << " with index: " << i + 1 << " SENT" << std::endl;
// Avoid unmatched condition impact subscriber receiving message
std::cout << "Wait within twenty second..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
}
++i;
}
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
}
}
void PicturePublisher::run(
uint32_t samples,
uint32_t sleep)
{
stop = false;
std::thread thread(&PicturePublisher::runThread, this, samples, sleep);
if (samples == 0)
{
std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl;
std::cin.ignore();
stop = true;
}
else
{
std::cout << "Publisher running " << samples << " samples." << std::endl;
}
thread.join();
}
bool PicturePublisher::publish(
bool waitForListener)
{
// std::cout << "m_listener.n_matched: " << m_listener.n_matched << std::endl;
if (m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0)
{
uint32_t index;
m_DynHello->get_uint32_value(index, 0);
m_DynHello->set_uint32_value(index + 1, 0);
mp_publisher->write((void*)m_DynHello);
return true;
}
return false;
}
In PicturePublisher::init() function
Subsciber.cpp
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// Hpshboss modifys code from eprosima's github example;
// Licensed under the Apache License, Version 2.0 (the "License");
/**
* #file Subscriber.cpp
*
*/
#include "Subscriber.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/SubscriberAttributes.h>
#include <fastrtps/subscriber/Subscriber.h>
#include <fastrtps/Domain.h>
#include <fastrtps/types/DynamicTypeBuilderFactory.h>
#include <fastrtps/types/DynamicDataFactory.h>
#include <fastrtps/types/DynamicTypeBuilder.h>
#include <fastrtps/types/DynamicTypeBuilderPtr.h>
#include <fastrtps/types/DynamicType.h>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include <opencv2/opencv.hpp>
using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;
using namespace eprosima::fastrtps::types;
// using namespace cv;
PictureSubscriber::PictureSubscriber()
: mp_participant(nullptr)
, mp_subscriber(nullptr)
, m_DynType(DynamicType_ptr(nullptr))
{
}
struct timespec begin, end;
double elapsed;
std::vector<unsigned char> buffer;
bool PictureSubscriber::init()
{
ParticipantAttributes PParam;
PParam.rtps.setName("DynPicture_sub");
mp_participant = Domain::createParticipant(PParam, (ParticipantListener*)&m_part_list);
if (mp_participant == nullptr)
{
return false;
}
// Create basic builders
DynamicTypeBuilder_ptr struct_type_builder(DynamicTypeBuilderFactory::get_instance()->create_struct_builder());
DynamicTypeBuilder_ptr octet_builder(DynamicTypeBuilderFactory::get_instance()->create_byte_builder());
DynamicTypeBuilder_ptr sequence_type_builder(DynamicTypeBuilderFactory::get_instance()->create_sequence_builder(octet_builder.get(), 3873715));
DynamicType_ptr sequence_type = sequence_type_builder->build();
// Add members to the struct.
struct_type_builder->add_member(0, "index", DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(1, "size", DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(2, "Picture", sequence_type);
struct_type_builder->set_name("Picture");
DynamicType_ptr dynType = struct_type_builder->build();
m_DynType.SetDynamicType(dynType);
m_listener.m_DynHello = DynamicDataFactory::get_instance()->create_data(dynType);
//REGISTER THE TYPE
Domain::registerDynamicType(mp_participant, &m_DynType);
//CREATE THE SUBSCRIBER
SubscriberAttributes Rparam;
Rparam.topic.topicKind = NO_KEY;
Rparam.topic.topicDataType = "Picture";
Rparam.topic.topicName = "PictureTopic";
mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, (SubscriberListener*)&m_listener);
if (mp_subscriber == nullptr)
{
return false;
}
return true;
}
PictureSubscriber::~PictureSubscriber()
{
Domain::removeParticipant(mp_participant);
DynamicDataFactory::get_instance()->delete_data(m_listener.m_DynHello);
Domain::stopAll();
}
void PictureSubscriber::SubListener::onSubscriptionMatched(
Subscriber* /*sub*/,
MatchingInfo& info)
{
if (info.status == MATCHED_MATCHING)
{
n_matched++;
std::cout << "Subscriber matched" << std::endl;
}
else
{
n_matched--;
std::cout << "Subscriber unmatched" << std::endl;
}
}
void PictureSubscriber::PartListener::onParticipantDiscovery(
Participant*,
ParticipantDiscoveryInfo&& info)
{
if (info.status == ParticipantDiscoveryInfo::DISCOVERED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " discovered" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::REMOVED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " removed" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::DROPPED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " dropped" << std::endl;
}
}
void PictureSubscriber::SubListener::onNewDataMessage(
Subscriber* sub)
{
if (sub->takeNextData((void*)m_DynHello, &m_info))
{
if (m_info.sampleKind == ALIVE)
{
this->n_samples++;
// Print your structure data here.
uint32_t index;
m_DynHello->get_uint32_value(index, 0);
std::cout << "index: " << index << "; \t";
uint32_t size;
m_DynHello->get_uint32_value(size, 1);
std::cout << "size: " << size << std::endl;
DynamicType_ptr octet_type_temp(DynamicTypeBuilderFactory::get_instance()->create_byte_type());
DynamicTypeBuilder_ptr sequence_type_builder_temp(DynamicTypeBuilderFactory::get_instance()->create_sequence_builder(octet_type_temp, 3873715));
DynamicType_ptr sequence_type_temp = sequence_type_builder_temp->build();
DynamicData* sequence_data_temp = m_DynHello->loan_value(2);
for (int i = 0; i < size; i++) {
buffer.push_back(sequence_data_temp->get_byte_value(i));
}
m_DynHello->return_loaned_value(sequence_data_temp);
cv::Mat imageDecoded = cv::imdecode(buffer, 1);
cv::imwrite(std::to_string(index) + "_droneNew.jpg", imageDecoded);
}
}
}
void PictureSubscriber::run()
{
std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl;
std::cin.ignore();
}
void PictureSubscriber::run(
uint32_t number)
{
std::cout << "Subscriber running until " << number << "samples have been received" << std::endl;
while (number > this->m_listener.n_samples)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
In PictureSubscriber::SubListener::onNewDataMessage(Subscriber* sub) function
Here at eProsima, we have found some solutions to the problem you point out.
Firstly, please note that you don't need to use Dynamic Types to define the type that contains the image you are going to transmit. The easiest thing to do in your case is to define your type through an IDL file. Using the IDL file and the Fast-DDS-Gen tool you can generate the code for access to the data type elements, as well as automatically generate the data serialization and deserialization functions. In the Picture.idl file you will find the type defined in IDL format that best suits the data type you have created with dynamic types. Here you can find a guide on how to use the Fast-DDS-Gen tool. In this documentation you will also find a complete example of how an IDL file can be used to generate a complete DDS publisher/subscriber application, as well as the supported formats for the data. Also below are the files Publisher.cpp and Subscriber.cpp which have been modified according to the new data type.
We also recommend you to take a look at the example HelloWorldExample, as it is the one that best suits your needs. In this example you can also discover the new DDS API, included in the latest version of Fast DDS (2.1.0).
As an additional comment, we recommend that, instead of transmitting an octet vector, you encode the image in string base64 format before transmitting it since it's one of the most widespread formats for image transmission.
Picture.idl
struct Picture
{
unsigned long index;
unsigned long size;
sequence<octet> picture;
};
Publisher.cpp
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// Hpshboss modifys code from eprosima's github example;
// Licensed under the Apache License, Version 2.0 (the "License");
/**
* #file PicturePublisher.cpp
*
*/
#include "PicturePublisher.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/PublisherAttributes.h>
#include <fastrtps/publisher/Publisher.h>
#include <fastrtps/Domain.h>
#include <fastrtps/types/DynamicTypeBuilderFactory.h>
#include <fastrtps/types/DynamicDataFactory.h>
#include <fastrtps/types/DynamicTypeBuilder.h>
#include <fastrtps/types/DynamicTypeBuilderPtr.h>
#include <fastrtps/types/DynamicType.h>
#include <thread>
#include <time.h>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;
using namespace eprosima::fastrtps::types;
// using namespace cv;
PicturePublisher::PicturePublisher()
: mp_participant(nullptr)
, mp_publisher(nullptr)
{
}
bool PicturePublisher::init()
{
cv::Mat image = cv::imread("dog.jpg", cv::IMREAD_COLOR);
if(image.empty())
{
std::cout << "Could not read the image." << std::endl;
return false;
}
cv::imshow("Display window", image);
int k = cv::waitKey(0);
std::vector<unsigned char> buffer;
if(!cv::imencode(".jpg", image, buffer)){
printf("Image encoding failed");
}
m_Picture.index(0);
m_Picture.size(buffer.size());
m_Picture.picture(buffer);
ParticipantAttributes PParam;
PParam.rtps.setName("Picture_pub");
mp_participant = Domain::createParticipant(PParam, &m_part_list);
if (mp_participant == nullptr)
{
return false;
}
//REGISTER THE TYPE
Domain::registerType(mp_participant, &m_type);
// Domain::registerDynamicType(mp_participant, &m_DynType);
//CREATE THE PUBLISHER
PublisherAttributes Wparam;
Wparam.topic.topicKind = NO_KEY;
Wparam.topic.topicDataType = "Picture";
Wparam.topic.topicName = "PictureTopic";
mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);
if (mp_publisher == nullptr)
{
return false;
}
return true;
}
PicturePublisher::~PicturePublisher()
{
Domain::removeParticipant(mp_participant);
}
void PicturePublisher::PubListener::onPublicationMatched(
Publisher* /*pub*/,
MatchingInfo& info)
{
if (info.status == MATCHED_MATCHING)
{
n_matched++;
firstConnected = true;
std::cout << "Publisher matched" << std::endl;
}
else
{
n_matched--;
std::cout << "Publisher unmatched" << std::endl;
}
}
void PicturePublisher::PartListener::onParticipantDiscovery(
Participant*,
ParticipantDiscoveryInfo&& info)
{
if (info.status == ParticipantDiscoveryInfo::DISCOVERED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " discovered" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::REMOVED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " removed" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::DROPPED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " dropped" << std::endl;
}
}
void PicturePublisher::runThread(
uint32_t samples,
uint32_t sleep)
{
uint32_t i = 0;
while (!stop && (i < samples || samples == 0))
{
if (publish(samples != 0))
{
std::cout << "runThreading...; \tSample Index: " << m_Picture.index() << "; \t";
std::cout << "size: " << m_Picture.size() << std::endl;
if (i == 9){
std::cout << "Structure message" << " with index: " << i + 1 << " SENT" << std::endl;
// Avoid unmatched condition impact subscriber receiving message
std::cout << "Wait within twenty second..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
}
++i;
}
std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
}
}
void PicturePublisher::run(
uint32_t samples,
uint32_t sleep)
{
stop = false;
std::thread thread(&PicturePublisher::runThread, this, samples, sleep);
if (samples == 0)
{
std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl;
std::cin.ignore();
stop = true;
}
else
{
std::cout << "Publisher running " << samples << " samples." << std::endl;
}
thread.join();
}
bool PicturePublisher::publish(
bool waitForListener)
{
// std::cout << "m_listener.n_matched: " << m_listener.n_matched << std::endl;
if (m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0)
{
m_Picture.index(m_Picture.index() + 1);
mp_publisher->write((void*)&m_Picture);
return true;
}
return false;
}
Subscriber.cpp
// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// Hpshboss modifys code from eprosima's github example;
// Licensed under the Apache License, Version 2.0 (the "License");
/**
* #file Subscriber.cpp
*
*/
#include "PictureSubscriber.h"
#include <fastrtps/attributes/ParticipantAttributes.h>
#include <fastrtps/attributes/SubscriberAttributes.h>
#include <fastrtps/subscriber/Subscriber.h>
#include <fastrtps/Domain.h>
#include <fastrtps/types/DynamicTypeBuilderFactory.h>
#include <fastrtps/types/DynamicDataFactory.h>
#include <fastrtps/types/DynamicTypeBuilder.h>
#include <fastrtps/types/DynamicTypeBuilderPtr.h>
#include <fastrtps/types/DynamicType.h>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include <opencv2/opencv.hpp>
using namespace eprosima::fastrtps;
using namespace eprosima::fastrtps::rtps;
using namespace eprosima::fastrtps::types;
// using namespace cv;
PictureSubscriber::PictureSubscriber()
: mp_participant(nullptr)
, mp_subscriber(nullptr)
{
}
struct timespec begin, end;
double elapsed;
std::vector<unsigned char> buffer;
bool PictureSubscriber::init()
{
ParticipantAttributes PParam;
PParam.rtps.setName("Picture_sub");
mp_participant = Domain::createParticipant(PParam, &m_part_list);
if (mp_participant == nullptr)
{
return false;
}
//REGISTER THE TYPE
Domain::registerType(mp_participant, &m_type);
//CREATE THE SUBSCRIBER
SubscriberAttributes Rparam;
Rparam.topic.topicKind = NO_KEY;
Rparam.topic.topicDataType = "Picture";
Rparam.topic.topicName = "PictureTopic";
mp_subscriber = Domain::createSubscriber(mp_participant, Rparam, (SubscriberListener*)&m_listener);
if (mp_subscriber == nullptr)
{
return false;
}
return true;
}
PictureSubscriber::~PictureSubscriber()
{
Domain::removeParticipant(mp_participant);
}
void PictureSubscriber::SubListener::onSubscriptionMatched(
Subscriber* /*sub*/,
MatchingInfo& info)
{
if (info.status == MATCHED_MATCHING)
{
n_matched++;
std::cout << "Subscriber matched" << std::endl;
}
else
{
n_matched--;
std::cout << "Subscriber unmatched" << std::endl;
}
}
void PictureSubscriber::PartListener::onParticipantDiscovery(
Participant*,
ParticipantDiscoveryInfo&& info)
{
if (info.status == ParticipantDiscoveryInfo::DISCOVERED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " discovered" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::REMOVED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " removed" << std::endl;
}
else if (info.status == ParticipantDiscoveryInfo::DROPPED_PARTICIPANT)
{
std::cout << "Participant " << info.info.m_participantName << " dropped" << std::endl;
}
}
void PictureSubscriber::SubListener::onNewDataMessage(
Subscriber* sub)
{
std::cout << "Data received." << std::endl;
if (sub->takeNextData((void*)&m_Picture, &m_info))
{
if (m_info.sampleKind == ALIVE)
{
this->n_samples++;
// Print your structure data here.
uint32_t index = m_Picture.index();
std::cout << "index: " << index << "; \t";
std::cout << "size: " << m_Picture.size() << std::endl;
cv::Mat imageDecoded = cv::imdecode(m_Picture.picture(), 1);
cv::imwrite(std::to_string(index) + "_dog_received.jpg", imageDecoded);
}
}
}
void PictureSubscriber::run()
{
std::cout << "Subscriber running. Please press enter to stop the Subscriber" << std::endl;
std::cin.ignore();
}
void PictureSubscriber::run(
uint32_t number)
{
std::cout << "Subscriber running until " << number << "samples have been received" << std::endl;
while (number > this->m_listener.n_samples)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}

Given a C function that populates a unsigned char**, how to populate a std::vector with the data without an intermediate copy

The C functions are:
int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp);
int i2d_X509(X509 *a, unsigned char **ppout);
And I have written code like this to copy into the std::vector :
// populate PrivateKey
std::vector<uint8_t> PrivateKey;
EVP_PKEY *privatekey = NULL;
int size = i2d_PrivateKey(privatekey, NULL);
if (size > 0)
{
PrivateKey.reserve(size);
uint8_t* ptr = &PrivateKey[0];
i2d_PrivateKey(privatekey, &ptr);
std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
}
PrivateKey.size() returns zero so I know that the vector has not been populated. However, I know that size is a positive integer so the code inside the if (size block is executed.
If ptr is the address of the start of the PrivateKey array then shouldn't this code work?
Although this code uses openssl, it is more a general pointers question I think. If I create a temporary uint8_t array then it works but I would rather copy directly into the vector and save the overhead of the temporary copy.
Here is the code:
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
int main()
{
std::vector<uint8_t> input; // contains pkcs12 data
std::string Password = "password";
std::vector<uint8_t> Certificate;
std::vector<uint8_t> PrivateKey;
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
ERR_load_crypto_strings();
PKCS12* p12_cert = NULL;
const uint8_t* p1 = &input[0];
if(d2i_PKCS12(&p12_cert, &p1, input.size()) != NULL) {
EVP_PKEY *privatekey = NULL;
X509 *x509_cert = NULL;
// additional certs, last arg is CA which we don't care about
if (PKCS12_parse(p12_cert, Password.c_str(), &privatekey, &x509_cert, NULL))
{
// populate m_privateKey
int size = i2d_PrivateKey(privatekey, NULL);
std::cout << "privatekey size=" << size << '\n';
if (size > 0)
{
PrivateKey.reserve(size);
uint8_t* ptr = &PrivateKey[0];
i2d_PrivateKey(privatekey, &ptr);
std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
}
// populate certificate
size = i2d_X509(x509_cert, NULL);
std::cout << "certificate size=" << size << '\n';
if(size > 0)
{
Certificate.reserve(size);
uint8_t* ptr = &Certificate[0];
int ret = i2d_X509(x509_cert, &ptr);
std::cout << "ret=" << ret <<'\n';
std::cout << "cert size=" << Certificate.size() << '\n';
}
}
PKCS12_free(p12_cert);
}
}
UPDATE, can use code below to incorporate Arron's fix:
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
#include <iostream>
#include <fstream>
#include <iterator>
#include <iomanip>
#include <vector>
#include <string>
#include <cstdint>
using namespace std;
std::vector<uint8_t>& File2Buffer(const std::string path,
std::vector<uint8_t>& buffer) {
fstream fs(path, ios::in | ios::binary);
if (fs) {
// Don't skip new lines
fs.unsetf(ios::skipws);
fs.seekg(0, ios::end);
size_t size = static_cast<size_t>(fs.tellg());
fs.seekg(0, ios::beg);
buffer.reserve(size);
buffer.insert(buffer.begin(),
istream_iterator<uint8_t>(fs),
istream_iterator<uint8_t>());
}
return buffer;
}
int main(int argc, char* argv[])
{
if (argc != 3) {
cout << "Usage: " << argv[0] << " <pkcs12 file> " << "<password>\n";
exit(0);
}
std::vector<uint8_t> input;
File2Buffer(argv[1], input);
std::string Password = argv[2];
std::vector<uint8_t> Certificate;
std::vector<uint8_t> PrivateKey;
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
ERR_load_crypto_strings();
PKCS12* p12_cert = NULL;
const uint8_t* p1 = &input[0];
if(d2i_PKCS12(&p12_cert, &p1, input.size()) != NULL) {
EVP_PKEY *privatekey = NULL;
X509 *x509_cert = NULL;
// additional certs, last arg is CA which we don't care about
if (PKCS12_parse(p12_cert, Password.c_str(), &privatekey, &x509_cert, NULL))
{
// populate m_privateKey
int size = i2d_PrivateKey(privatekey, NULL);
std::cout << "privatekey size=" << size << '\n';
if (size > 0)
{
PrivateKey.resize(size);
uint8_t* ptr = &PrivateKey[0];
i2d_PrivateKey(privatekey, &ptr);
std::cout << "PrivateKey size=" << PrivateKey.size() << '\n';
}
// populate certificate
size = i2d_X509(x509_cert, NULL);
std::cout << "certificate size=" << size << '\n';
if(size > 0)
{
Certificate.resize(size);
uint8_t* ptr = &Certificate[0];
int ret = i2d_X509(x509_cert, &ptr);
std::cout << "ret=" << ret <<'\n';
std::cout << "cert size=" << Certificate.size() << '\n';
}
}
PKCS12_free(p12_cert);
}
// test it out:
if (Certificate.size() > 0) {
cout << "Certificate size=" << Certificate.size() << '\n';
for (auto& ch : Certificate) {
cout << hex << ch << " ";
}
}
}
Use resize instead of reserve. The problem with reserve is that if you do an assign(like PrivateKey[5] = 5), and call PrivateKey.size(), size will still remain 0.
(Reserve in practice can be used in pair with back_inserter in std::copy), but in your case, you should do an resize.

XML parser wrapper

Is there any kind of XML parser wrapper library that would allow switching the actual XML parser engine at configuration or run time instead of forcing me to choose between libxml2, expat or Xalan-C++?
I wrote something similar a while back:
struct xerces;
struct msxml;
struct rapid;
struct tiny;
struct pugixml;
template <typename T> struct platform_manager;
template <typename T> double parse_file(std::string const& f, QueryPerfCounter& qpc);
template<class T>
void demo(std::string const& f, size_t N = 10) {
platform_manager<T> pm;
QueryPerfCounter qpc;
std::vector<double> timing_data;
timing_data.reserve(N);
std::generate_n(std::back_inserter(timing_data), N, std::tr1::bind(&parse_file<typename T>, f, qpc));
adobe::Statistics<double> s(timing_data.begin(), timing_data.end());
std::cout << "Iteration count: " << s.count() << " Mean time: " << s.mean() << "s. Variance: " << s.variance() << "s.\n";
}
/***************************************************************/
template <>
struct platform_manager<msxml> {
platform_manager() {
if (FAILED(CoInitialize(NULL)))
throw std::runtime_error("CoCreateInstance failed");
}
~platform_manager() {
CoUninitialize();
}
};
template<>
double parse_file<msxml>(std::string const& f, QueryPerfCounter& qpc) {
CComPtr<IXMLDOMDocument> pXMLDom;
HRESULT hr = CoCreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDom));
CComPtr<IXMLDOMParseError> pXMLErr;
VARIANT_BOOL varStatus;
qpc.Start();
if (FAILED(pXMLDom->load(CComVariant(f.c_str()), &varStatus)))
std::cout << "Parsing failed" << std::endl;
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#ifdef XERCES_CPP_NAMESPACE_USE
XERCES_CPP_NAMESPACE_USE
#endif
template <>
struct platform_manager<xerces> {
platform_manager() try {
XMLPlatformUtils::Initialize();
} catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Failed to init: " << XMLString::transcode(message) << std::endl;
XMLString::release(&message);
}
~platform_manager() {
XMLPlatformUtils::Terminate();
}
};
template<>
double parse_file<xerces>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
std::tr1::shared_ptr<XercesDOMParser> parser(new XercesDOMParser());
parser->setValidationScheme(XercesDOMParser::Val_Always);
parser->setDoNamespaces(true); // optional
std::tr1::shared_ptr<ErrorHandler> errHandler(new HandlerBase());
parser->setErrorHandler(errHandler.get());
try {
qpc.Start();
parser->parse(f.c_str());
qpc.Stop();
duration = qpc.Duration(QueryPerfCounter::seconds);
}
catch (const XMLException& toCatch) {
char* message = XMLString::transcode(toCatch.getMessage());
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (const DOMException& toCatch) {
char* message = XMLString::transcode(toCatch.msg);
std::cout << "Exception message is: \n"
<< message << "\n";
XMLString::release(&message);
}
catch (...) {
std::cout << "Unexpected Exception \n" ;
}
return duration;
}
/***************************************************************/
#include "rapidxml.hpp"
#include <vector>
#include <fstream>
#include <iterator>
template <>
struct platform_manager<rapid> {};
enum size_hint { B = 1, KB = 1024, MB = 1024 * 1024 };
double file_size(std::ifstream& f, size_hint factor = MB) {
f.seekg (0, std::ios::end);
size_t length = f.tellg();
f.seekg (0, std::ios::beg);
return double(length) / factor;
}
template<>
double parse_file<rapid>(std::string const& f, QueryPerfCounter& qpc) {
double duration = 0;
rapidxml::xml_document<> doc;
try {
qpc.Start();
std::ifstream myfile(f.c_str());
myfile.seekg (0, std::ios::end);
size_t length = myfile.tellg();
myfile.seekg (0, std::ios::beg);
std::vector<char> buffer(length);
myfile.read(& buffer[0], length);
//buffer.reserve(length);
//buffer.insert(std::istreambuf_iterator<char>(myfile)), std::istreambuf_iterator<char>( ));
//std::copy(std::istreambuf_iterator<char>(myfile), std::istreambuf_iterator<char>( ), std::back_insert_iterator(buffer));
buffer.push_back('\0');
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
//std::cout << "Buffer load time: " << duration << "s" << std::endl;
//QueryPerfCounter qpc;
qpc.Start();
doc.parse<rapidxml::parse_non_destructive>(&buffer[0]);
qpc.Stop();
duration += qpc.Duration(QueryPerfCounter::seconds);
} catch (rapidxml::parse_error const& e) {
std::cout << e.what() << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
return duration;
}
/***************************************************************/
template <>
struct platform_manager<tiny> {};
template<>
double parse_file<tiny>(std::string const& f, QueryPerfCounter& qpc) {
tinyxml2::XMLDocument doc;
qpc.Start();
doc.LoadFile(f.c_str());
doc.PrintError(); // emits nothing on success
qpc.Stop();
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
struct banner_printer {
banner_printer(std::string const& libname, std::string const& input) : lib(libname), in(input) {
std::cout << "/*+------------------- BEGIN test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
~banner_printer() {
std::cout << "/*+------------------- END test for " << lib << " with file: " << in << " -------------------+*/" << std::endl;
}
private:
std::string lib, in;
};
/***************************************************************/
#include "pugixml.hpp"
template <>
struct platform_manager<pugixml> {};
template<>
double parse_file<pugixml>(std::string const& f, QueryPerfCounter& qpc) {
pugi::xml_document doc;
qpc.Start();
pugi::xml_parse_result result = doc.load_file(f.c_str());
qpc.Stop();
if (!result) {
std::cout << "XML [" << f << "] parsed with errors, attr value: [" << doc.child("node").attribute("attr").value() << "]\n";
std::cout << "Error description: " << result.description() << "\n";
std::cout << "Error offset: " << result.offset << " (error at offset [..." << (result.offset) << "]\n\n";
}
return qpc.Duration(QueryPerfCounter::seconds);
}
/***************************************************************/
int main() {
std::vector<std::string> v = parse_catalog("D:/Work/xml_parsers/perfcompare/benchmark/catalog.txt");
std::for_each(v.begin(), v.end(), [](std::string const& s) {
{
std::ifstream f(s);
std::cout << "Input file name: " << s << " size: " << file_size(f) << "MB\n\n";
}
{
banner_printer b("xerces", s);
demo<xerces>(s);
}
{
banner_printer b("rapid", s);
demo<rapid>(s);
}
{
banner_printer b("tiny", s);
demo<tiny>(s);
}
{
banner_printer b("pugi", s);
demo<pugixml>(s);
}
{
banner_printer b("MSXML6", s);
demo<msxml>(s);
}
}
);
//expat_demo(argc, argv);
return 0;
}
It may or may not help you get started. I've skipped header includes and some other trivia. I tried to keep the interface simple and identical. This meant that some libraries required additional helper functions.