I'm trying to write and read serialized data to file, but it always breaks when reading specific "PNG" struct, with stored "bed_feet_top.png" from minecraft 1.8.9 textures folder in it, and I can't find a way to get the error. Already tried errno and try and catch, but it always says sucess. It seems that it breaks after and not when reading the buggy "PNG" struct, because the read data is uncorrupted and I can recreate the stored image. I'm using "stb_image" library to read the image data and "zpp_bits" library for serializing the data.
the code i would use:
PNG srcImage; //The "PNG" struct it will break at
unsigned char* buffer = stbi_load("bed_feet_top.png", &image.imageWidth, &image.imageHeight, &image.colorChannels, STBI_rgb_alpha);
if (!buffer)
{
std::cout << "Failed to read image: " + path << std::endl;
return 1;
}
image.pixels = (char*)buffer;
DataBuffer srcBuffer;
srcBuffer.serialize("somePath", "someName", srcImage);
DataManager::writeToFile("someFile.dat", srcBuffer);
DataBuffer dstBuffer;
DataManager::readFromFile("someFile.dat", &dstbuffer);
PNG dstImage = dstBuffer.deserialize<PNG>("somePath", "someName"); //The buggy "PNG" struct data is uncorrupted
the "PNG" struct:
struct PNG
{
int imageWidth, imageHeight, colorChannels;
std::string pixels;
};
the struct I'm storing all serialized data in:
struct DataBuffer
{
std::map<std::string, std::vector<std::byte>> data; //All the serialized data is stored in this map
template<typename T>
void serialize(std::string path, std::string name, T obj)
{
path.append("/");
path.append(name);
serialize(path, obj);
}
template<typename T>
void serialize(std::string fullPath, T obj)
{
if (data.contains(fullPath))
{
std::cout << "Data buffer already contains value with te same path!" << std::endl;
return;
}
try
{
zpp::bits::out out(data[fullPath]);
out(obj).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Serialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Serialization failed with unknown error!" << std::endl;
}
}
template<typename T>
T deserialize(std::string path, std::string name)
{
path.append("/");
path.append(name);
return deserialize<T>(path);
}
template<typename T>
T deserialize(std::string fullPath)
{
T obj;
if (!data.contains(fullPath))
{
std::cout << "Data buffer doesn't contain value with the given path!" << std::endl;
return obj;
}
try
{
zpp::bits::in in(data[fullPath]);
in(obj).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Deserialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Deserialization failed with unknown error!" << std::endl;
}
return obj;
}
};
The write to file function:
void DataManager::writeToFile(std::string fileName, DataBuffer dataBuffer)
{
std::ofstream ofs(fileName, std::ios::out | std::ios::binary);
if (!ofs.is_open())
{
std::cerr << "Failed to open output file stream!" << std::endl;
return;
}
size_t dataCount = dataBuffer.data.size();
ofs.write((char*)&dataCount, 8);
for (auto const& [key, val] : dataBuffer.data)
{
size_t keySize = key.size();
ofs.write((char*)&keySize, sizeof(keySize));
ofs.write(&key[0], keySize);
size_t entryCount = val.size();
ofs.write((char*)&entryCount, 8);
ofs.write((char*)val.data(), entryCount);
if (!ofs.good()) //ofs.good() never returned false to me
{
std::cerr << "Something went wrong when writing " << key << " to the file!" << std::endl;
break;
}
}
ofs.close();
}
the read from file function:
void DataManager::readFromFile(std::string fileName, DataBuffer* dataBuffer)
{
std::ifstream ifs(fileName, std::ios::in, std::ios::binary);
if (!ifs)
{
std::cerr << "File doesnt exist!" << std::endl;
return;
}
if (!ifs.is_open())
{
std::cerr << "Failed to open input file stream!" << std::endl;
return;
}
size_t dataCount;
ifs.read((char*)&dataCount, 8);
for (size_t i = 0; i < dataCount; i++)
{
size_t keySize;
ifs.read((char*)&keySize, sizeof(keySize));
std::string key;
key.resize(keySize);
ifs.read(&key[0], keySize);
size_t entryCount;
ifs.read((char*)&entryCount, 8);
dataBuffer->data[key].resize(entryCount);
ifs.read((char*)dataBuffer->data[key].data(), entryCount);
if (!ifs.good()) //ifs.good() returns false after it reads the buggy "PNG" struct data
{
std::cerr << "Something went wrong when reading " << key << " from the file!" << std::endl;
break;
}
}
ifs.close();
}
EDIT
I added minimal reproducible example as "Igor Tandetnik" requested.
the minimal reproducible example:
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <stb_image.h>
#include "stb_image_write.h"
#include <zpp_bits.h>
struct PNG
{
int imageWidth, imageHeight, colorChannels;
std::string pixels;
};
int main()
{
std::string fileName = "someFile.dat"; //The file we will store the image data in
std::string srcImageName = "bed_feet_top.png"; //Texture from Minecraft 1.8.9 textures folder
std::string dstImageName = "someImage.png"; //The name of image we will create from the read data
//Reading the image data
PNG srcImage;
unsigned char* buffer = stbi_load(srcImageName.c_str(), &srcImage.imageWidth, &srcImage.imageHeight, &srcImage.colorChannels, STBI_rgb_alpha);
if (!buffer)
{
std::cout << "Failed to read image " + srcImageName << std::endl;
return 1;
}
int dataSize = srcImage.imageWidth * srcImage.imageHeight * srcImage.colorChannels;
srcImage.pixels.assign((char*)buffer, dataSize);
//Serializing the image data
std::vector<std::byte> srcData;
try
{
zpp::bits::out out(srcData);
out(srcImage).or_throw();
}
catch (const std::exception& error)
{
std::cerr << "Serialization failed with error: " << error.what() << std::endl;
}
catch (...) {
std::cerr << "Serialization failed with unknown error!" << std::endl;
}
//Writing the image data to file
std::ofstream ofs(fileName, std::ios::out | std::ios::binary);
if (!ofs.is_open())
{
std::cout << "Failed to open output file stream!" << std::endl;
return 1;
}
size_t srcEntryCount = srcData.size();
ofs.write((char*)&srcEntryCount, 8);
ofs.write((char*)srcData.data(), srcEntryCount);
if (!ofs.good())
{
std::cerr << "Something went wrong when writing" << srcImageName << " to the file!" << std::endl;
ofs.close();
return 1;
}
ofs.close();
//Reading the image data from file
std::ifstream ifs(fileName, std::ios::in, std::ios::binary);
if (!ifs)
{
std::cout << "File doesnt exist!" << std::endl;
return 1;
}
if (!ifs.is_open())
{
std::cout << "Failed to open input file stream!" << std::endl;
return 1;
}
std::vector<std::byte> dstData;
size_t dstEntryCount;
ifs.read((char*)&dstEntryCount, 8);
dstData.resize(dstEntryCount);
ifs.read((char*)dstData.data(), dstEntryCount);
if (!ifs.good())
{
std::cerr << "Something went wrong when reading " << srcImageName << " from the file!" << std::endl;
ifs.close();
return 1; //If you comment this line you will see it will recreate the image from the read data just fine
}
ifs.close();
//Deserializing the read image data
PNG dstImage;
try
{
zpp::bits::in in(dstData);
in(dstImage).or_throw();
} catch (const std::exception& error)
{
std::cerr << "Deserialization failed with error: " << error.what() << std::endl;
} catch (...) {
std::cerr << "Deserialization failed with unknown error!" << std::endl;
}
//Recreating the image from the read image data
stbi_write_png(dstImageName.c_str(), dstImage.imageWidth, dstImage.imageHeight, dstImage.colorChannels, dstImage.pixels.c_str(), dstImage.imageWidth * sizeof(int));
}
I'm caling the objects src and dst, because I don't know how to call them shortly before and after serialization. :D
Related
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.
Im having problems understanding how to write a PGM file using data.
This function i have overwrites the existing txt document instead of creating a PGM file.
Im unsure whether my issue is lies within my Read_text function or somewhere else. Can anyone help me understand this issue and fix it please?
double* read_text(char *fileName, int sizeR, int sizeC)
{
double* data = new double[sizeR*sizeC];
int i = 0;
std::ifstream myfile(fileName);
if (myfile.is_open())
{
while (myfile.good())
{
if (i>sizeR*sizeC - 1) break;
myfile >> *(data + i);
std::cout << *(data+i) << ' '; //This line display the converted data on the screen, you may comment it out.
i++;
}
myfile.close();
}
else std::cout << "Unable to open file";
std::cout << i;
return data;
}
void write_pgm(char *filename, double *data, int sizeR, int sizeC, int Q)
{
int i, j;
unsigned char *image;
std::ofstream myfile;
image = (unsigned char *) new unsigned char[sizeR*sizeC];
// convert the integer values to unsigned char
for (i = 0; i<sizeR*sizeC; i++)
image[i] = (unsigned char)data[i];
myfile.open(filename, std::ios::out | std::ios::binary | std::ios::trunc);
if (!myfile) {
std::cout << "Can't open file: " << filename << std::endl;
exit(1);
}
myfile << "P5" << std::endl;
myfile << sizeC << " " << sizeR << std::endl;
myfile << Q << std::endl;
myfile.write(reinterpret_cast<char *>(image), (sizeR*sizeC)*sizeof(unsigned char));
if (myfile.fail()) {
std::cout << "Can't write image " << filename << std::endl;
exit(0);
}
myfile.close();
std::cout << "Finsihed " << std::endl;
delete[] image;
}
void main() {
double* data = read_text("Wally_grey.txt", rows(), columns());
std::cout << data << std::endl;
write_pgm("Wally_grey.txt",data, rows(), columns(), 255);
getchar();
}
I'm trying to implement some simple function wrappers around OpenAL.
When trying to load a file piece by piece to stream it through several buffers, I'm somehow reading the eof.
#include <iostream>
#include <type_traits>
#include <cstring>
#include <fstream>
int32_t convert_to_int(char* buffer, std::size_t len, const std::endian endianess)
{
int32_t a = 0;
if(endianess == std::endian::little)
std::memcpy(&a, buffer, len);
else
for(std::size_t i = 0; i < len; ++i)
reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
return a;
}
bool load_wav_file_header(std::ifstream& file, uint8_t& channels, int32_t& sampleRate, uint8_t& bitsPerSample, int32_t& size)
{
char buffer[4];
if(!file.is_open())
return false;
// the RIFF
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read RIFF" << std::endl;
return false;
}
if(std::strncmp(buffer, "RIFF", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't begin with RIFF)" << std::endl;
return false;
}
// the size of the file
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read size of file" << std::endl;
return false;
}
// the WAVE
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read WAVE" << std::endl;
return false;
}
if(std::strncmp(buffer, "WAVE", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't contain WAVE)" << std::endl;
return false;
}
// "fmt/0"
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read fmt/0" << std::endl;
return false;
}
// this is always 16, the size of the fmt data chunk
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read the 16" << std::endl;
return false;
}
// PCM should be 1?
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read PCM" << std::endl;
return false;
}
// the number of channels
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read number of channels" << std::endl;
return false;
}
channels = convert_to_int(buffer, 2, std::endian::little);
// sample rate
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read sample rate" << std::endl;
return false;
}
sampleRate = convert_to_int(buffer, 4, std::endian::little);
// (sampleRate * bitsPerSample * channels) / 8
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read (sampleRate * bitsPerSample * channels) / 8" << std::endl;
return false;
}
// ?? dafaq
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read dafaq" << std::endl;
return false;
}
// bitsPerSample
if(!file.read(buffer, 2))
{
std::cerr << "ERROR: could not read bits per sample" << std::endl;
return false;
}
bitsPerSample = convert_to_int(buffer, 2, std::endian::little);
// data chunk header "data"
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read data chunk header" << std::endl;
return false;
}
if(std::strncmp(buffer, "data", 4) != 0)
{
std::cerr << "ERROR: file is not a valid WAVE file (doesn't have 'data' tag)" << std::endl;
return false;
}
// size of data
if(!file.read(buffer, 4))
{
std::cerr << "ERROR: could not read data size" << std::endl;
return false;
}
size = convert_to_int(buffer, 4, std::endian::little);
/* cannot be at the end of file */
if(file.eof())
{
std::cerr << "ERROR: reached EOF on the file" << std::endl;
return false;
}
if(file.fail())
{
std::cerr << "ERROR: fail state set on the file" << std::endl;
return false;
}
return true;
}
const int32_t BUFFER_SIZE = 320000;
const int32_t NUM_BUFFERS = 4;
int main()
{
std::string filename = "Assets/session.wav";
std::ifstream file;
file.open(filename);
if(!file.is_open())
{
std::cerr << "ERROR: could not open file \"" << filename << "\"" << std::endl;
return false;
}
uint8_t channels;
int32_t sampleRate;
uint8_t bitsPerSample;
int32_t size;
if(!load_wav_file_header(file,channels,sampleRate,bitsPerSample,size))
{
std::cerr << "ERROR: could not open wav header of \"" << filename << "\"" << std::endl;
return false;
}
char* data = new char[size];
if(!file.read(data, size))
{
if(file.eof())
std::cerr << "ERROR: reached end of file trying to read "
<< size << " bytes. Actually read " << file.gcount() << std::endl;
else if(file.fail())
std::cerr << "ERROR: fail state" << std::endl;
else if(file.bad())
perror(("error while reading file " + filename).c_str());
return 0;
}
return 0;
}
The output of the program is:
ERROR: reached end of file trying to read 7035488 bytes. Actually read 554
According to cppreference this happens with read when you reach the eof, which based on gcount(), it looks like I have. The problem is that this file is a total of 6,871 KB, and the reported size in the .WAV file is otherwise larger than the 554 it's claiming to have left in it.
I'm building a program that handles input file and output file, with Visual Studio 2012.
I implemented like this:
ifstream inputFile;
ofstream outputFile;
inputFile.exceptions ( ifstream::failbit | ifstream::badbit );
try
{
// some codes here
inputFile.open(inputFileName.c_str());
cout << "Input file opened" << endl;
outputFile.open(outputFileName.c_str());
cout << "Output file opened" << endl;
}
catch (ifstream::failure e)
{
cerr << "Failed to open input file" << endl;
return -1;
}
catch (ofstream::failure e)
{
cerr << "Failed to open output file" << endl;
return -1;
}
And a compile error occurs:
error C2312: 'std::ios_base::failure' : is caught by 'std::ios_base::failure' at line 248
How do I implement try-catch with two sources of exception?
Your problem is that ifstream::failure and ofstream::failure are the same type (inherited to both of them from ios_base),
Since it is the same exception, the compiler complains.
BTW you should catch by const reference to avoid unneeded copies.
As you can see, the types of the exceptions thrown are the same. But, as your checking is done so near the opening of the file, you can do it without exceptions, no? like:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
string inputFileName { "/notexists" };
string outputFileName { "/notexistsandprobablynotwritable" };
ifstream inputFile { inputFileName };
if( !inputFile ) {
cerr << "Failed to open input file" << endl;
return -1;
}
cout << "Input file opened" << endl;
ofstream outputFile { outputFileName };
if( !outputFile ) {
cerr << "Failed to open output file" << endl;
return -1;
}
cout << "Output file opened" << endl;
}
Or, if you really need the exceptions, you can throw different exceptions yourself, at the open site:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
template<typename Stream, typename Exception = typename Stream::failure>
void try_open(Stream& s, string filename) {
s.open(filename);
if( !s )
throw Exception( string("cannot open ")+filename );
}
struct input_exception: ifstream::failure { input_exception(const string& s): ifstream::failure(s) {} };
struct output_exception: ofstream::failure { output_exception(const string& s): ofstream::failure(s) {} };
int main() {
string inputFileName { "/notexists" };
string outputFileName { "/notexistsandprobablynotwritable" };
try {
ifstream inputFile;
try_open<ifstream, input_exception>(inputFile, inputFileName);
cout << "Input file opened" << endl;
ofstream outputFile;
try_open<ofstream, output_exception>(outputFile, outputFileName);
cout << "Output file opened" << endl;
} catch(const output_exception& e) {
cerr << "output exception!\n" << e.what() << "\n";
} catch(const input_exception& e) {
cerr << "input exception!\n" << e.what() << "\n";
} catch(const exception& e) {
cerr << "exception!\n" << e.what() << "\n";
}
}
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.