I used a DownloadManager class like that but the problem is that for large file I have a wrong size 65536KB instead of 51328022 bytes. There are something wrong in the saveDisk method.
ps: I used Qt 5.6 cuz I need to run the app on Windows Vista & XP
bool DownloadManager::saveToDisk(const QString &filename, QIODevice *data)
{
LogManager* logmgr = LogManager::GetInstance();
FileManager* filemgr = FileManager::GetInstance();
QFileInfo fileinfo(filename);
QString dirpath = fileinfo.absoluteDir().absolutePath();
if (!filemgr->DirectoryIsPresent(dirpath))
{
if (!filemgr->CreateDirectory(dirpath)) {
logmgr->Log(LOG_LEVEL_ERROR, QString("cannot create directory (") + dirpath + ")");
}
}
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
const QString errorText = QString("Could not open ") + filename + QString(" for writing:") + file.errorString();
logmgr->Log(LOG_LEVEL_ERROR, errorText);
return false;
}
file.write(data->readAll());
file.close();
return true;
}
void DownloadManager::downloadFinished(QNetworkReply *reply)
{
LogManager* logmgr = LogManager::GetInstance();
if (m_currentDownloads.contains(reply))
{
QUrl url = reply->url();
if (reply->error() != QNetworkReply::NoError) {
m_nbFailedDownload++;
const QString errorText = QString("Download of ")+ url.toString() +" failed: " + reply->errorString() + " (" + QString::number(reply->error()) + ")";
logmgr->Log(LOG_LEVEL_ERROR, errorText);
} else {
m_nbSucceededDownload++;
QString filename = saveFileName(url);
if (saveToDisk(filename, reply))
{
const QString infoText = QString("Download of ") + url.toString() + " succeeded (saved to " + filename + ")";
logmgr->Log(LOG_LEVEL_INFO, infoText);
}
}
m_currentDownloads.removeAll(reply);
reply->deleteLater();
}
int total = m_nbTotalDownload == 0? 1:m_nbTotalDownload;
emit onProgress((m_nbFailedDownload + m_nbSucceededDownload) * 100 / total);
if (m_currentDownloads.isEmpty()){
logmgr->Log(LOG_LEVEL_INFO, "DownloadManager downloads finished");
emit onFinished();
}
}
You should call DownloadManager::saveToDisk by signal QNetworkReply::readyRead
void DownloadManager::onDownloadReadyRead( QNetworkReply *reply )
{
...
saveToDisk( filename, reply );
...
}
void DownloadManager::saveToDisk( QNetworkReply* reply )
{
...
file.write( reply->read( reply_->bytesAvailable() ) );
...
}
It's ok. I replaced this line :
file.write(data->readAll());
file.close();
return true;
by
ByteArray ba = data->readAll();
int nbTries = 99;
int n = ba.size();
int idx = 0;
while (n > 0 && nbTries > 0)
{
int ret = file.write(ba.data() + idx, std::min(16*1024, n));
if (ret > 0)
{
n -= ret;
idx += ret;
}
else
{
nbTries --;
}
}
file.close();
return nbTries > 0;
I wrote several chunks instead of one big
Related
i am trying to create an API request to an AWS API, right now i fix the first issue that is related to generate de signature version 4, but no matter what i do, AWS response tell me that the signature is not welll generated, i already check the signature using nodejs code and works, according what i see and what AWS SUpport say, it is possible that some request headers are missing.
I am adding my QT Code i know that is not the best but is only an example, the function that i using to make the post request is loginWekall
#include "hash.h"
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QBuffer>
#include <QNetworkReply>
CryptoHash::CryptoHash(QObject *parent) : QObject(parent)
{
}
QString CryptoHash::getKey(){
return this->key;
}
void CryptoHash::setKey(QString key){
this->key = key;
}
void CryptoHash::setSecret(QString secret){
this->secret = secret;
}
void CryptoHash::setRegion(QString region){
this->region = region;
}
void CryptoHash::setMethod(QString method){
this->method = method;
}
void CryptoHash::setEndpoint(QString endpoint){
this->endpoint = endpoint;
}
void CryptoHash::setHost(QString host){
this->host = host;
}
void CryptoHash::setURL(QString URL){
this->url = URL;
}
QString CryptoHash::getHash() {
return this->hashvalue;
}
const QByteArray CryptoHash::getSignatureKey(QString datestamp) {
QMessageAuthenticationCode code(QCryptographicHash::Sha256);
code.setKey(datestamp.toUtf8());
code.addData("AWS4" + this->key.toUtf8());
QByteArray kDate = code.result();
code.reset();
code.setKey(this->region.toUtf8());
code.addData(kDate);
QByteArray kRegion = code.result();
code.reset();
code.setKey(this->service.toUtf8());
code.addData(kRegion);
QByteArray kService = code.result();
code.reset();
code.setKey("aws4_request");
code.addData(kService);
const QByteArray kSignin = code.result();
return kSignin;
}
QByteArray CryptoHash::algorithmDesignation(const QCryptographicHash::Algorithm algorithm) {
switch (algorithm) {
case QCryptographicHash::Md4: return "AWS4-HMAC-MD4";
case QCryptographicHash::Md5: return "AWS4-HMAC-MD5";
case QCryptographicHash::Sha1: return "AWS4-HMAC-SHA1";
case QCryptographicHash::Sha224: return "AWS4-HMAC-SHA224";
case QCryptographicHash::Sha256: return "AWS4-HMAC-SHA256";
case QCryptographicHash::Sha384: return "AWS4-HMAC-SHA384";
case QCryptographicHash::Sha512: return "AWS4-HMAC-SHA512";
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "invalid algorithm");
return "invalid-algorithm";
}
}
QByteArray CryptoHash::canonicalHeader(const QByteArray &headerName, const QByteArray &headerValue) {
QByteArray header = headerName.toLower() + ':';
const QByteArray trimmedHeaderValue = headerValue.trimmed();
bool isInQuotes = false;
char previousChar = '\0';
for (int index = 0; index < trimmedHeaderValue.size(); ++index) {
char thisChar = trimmedHeaderValue.at(index);
header += thisChar;
if (isInQuotes) {
if ((thisChar == '"') && (previousChar != '\\'))
isInQuotes = false;
} else {
if ((thisChar == '"') && (previousChar != '\\')) {
isInQuotes = true;
} else if (isspace(thisChar)) {
while ((index < trimmedHeaderValue.size()-1) &&
(isspace(trimmedHeaderValue.at(index+1))))
++index;
}
}
previousChar = thisChar;
}
return header;
}
QByteArray CryptoHash::canonicalHeaders(const QNetworkRequest &request, QByteArray * const signedHeaders) {
Q_CHECK_PTR(signedHeaders);
signedHeaders->clear();
QMap<QByteArray,QByteArray> headers;
foreach (const QByteArray &rawHeader, request.rawHeaderList()) {
headers.insert(rawHeader.toLower(), request.rawHeader(rawHeader));
}
headers.insert("host", request.url().host().toUtf8());
headers.insert("x-amz-date", this->amz.toUtf8());
QByteArray canonicalHeaders;
for (QMap<QByteArray,QByteArray>::const_iterator iter = headers.constBegin(); iter != headers.constEnd(); ++iter) {
canonicalHeaders += canonicalHeader(iter.key(), iter.value()) + '\n';
if (!signedHeaders->isEmpty()) *signedHeaders += ';';
*signedHeaders += iter.key();
}
return canonicalHeaders;
}
QByteArray CryptoHash::canonicalRequest(const QNetworkAccessManager::Operation operation,
const QNetworkRequest &request,
const QByteArray &payload,
QByteArray * const signedHeaders) {
//qInfo() <<"payload";
//qInfo() << payload;
//qInfo() << QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
return this->method.toUtf8() + '\n' +
this->endpoint.toUtf8() + '\n' +
"\n" + canonicalHeaders(request, signedHeaders) + '\n' +
*signedHeaders + '\n' +
QCryptographicHash::hash(payload, QCryptographicHash::Sha256).toHex();
}
QByteArray CryptoHash::credentialScope(const QDateTime &date, const QString ®ion, const QString &service) {
return this->dstamp.toUtf8() + '/' + region.toUtf8() + '/' + service.toUtf8() + "/aws4_request";
}
QByteArray CryptoHash::stringToSign(const QByteArray &algorithmDesignation, const QString &requestDate,
const QByteArray &credentialScope, const QByteArray &canonicalRequest) {
return algorithmDesignation + '\n' +
this->amz.toUtf8() + '\n' +
credentialScope + '\n' +
QCryptographicHash::hash(canonicalRequest, QCryptographicHash::Sha256).toHex();
}
QByteArray CryptoHash::signingKey( const QDate &date,
const QString ®ion, const QString &service) {
const QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Sha256;
return QMessageAuthenticationCode::hash("aws4_request",
QMessageAuthenticationCode::hash(service.toUtf8(),
QMessageAuthenticationCode::hash(region.toUtf8(),
QMessageAuthenticationCode::hash(this->dstamp.toUtf8(), "AWS4"+this->secret.toUtf8(),
hashAlgorithm), hashAlgorithm), hashAlgorithm), hashAlgorithm);
}
void debugRequest(QNetworkRequest request, QByteArray data = QByteArray())
{
qDebug() << request.url().toString();
const QList<QByteArray>& rawHeaderList(request.rawHeaderList());
foreach (QByteArray rawHeader, rawHeaderList) {
qDebug() << request.rawHeader(rawHeader);
}
qDebug() << data;
}
QString CryptoHash::loginWekall(const QString AWSV4, const QString timestamp){
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QUrl url("https://asdasdasd.execute-api.us-east-1.amazonaws.com/master/users/login");
QNetworkRequest request(url);
QJsonObject obj;
obj["email"] = "user#test.co";
obj["password"] = "password";
QJsonDocument doc(obj);
QByteArray data = doc.toJson();
// FIXME for debug
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Content-Type", "application/json");
request.setRawHeader("Authorization", AWSV4.toUtf8());
request.setRawHeader("X-Amz-Date", this->amz.toUtf8());
request.setRawHeader("Host","g298zqijvh.execute-api.us-east-1.amazonaws.com");
request.setRawHeader("Accept","application/json, text/plain, */*");
qDebug() << request.url().toString();
const QList<QByteArray>& rawHeaderList(request.rawHeaderList());
foreach (QByteArray rawHeader, rawHeaderList) {
qDebug() << request.rawHeader(rawHeader);
}
qDebug() << data;
QNetworkReply *reply = manager->post(request, data);
QObject::connect(reply, &QNetworkReply::finished, [=](){
if(reply->error() == QNetworkReply::NoError){
QString contents = QString::fromUtf8(reply->readAll());
qDebug() << contents;
}
else{
qInfo()<< QString::fromUtf8(reply->readAll());
QString err = reply->errorString();
qDebug() << err;
}
reply->deleteLater();
});
return "true";
}
QString CryptoHash::getDate(){
return this->amz;
}
QString CryptoHash::hash(){
const QDateTime datetime;
QDateTime* dtime = new QDateTime();
QString amzdate = dtime->currentDateTimeUtc().toString("yyyyMMddThhmmssZ").toUtf8();
QString datestamp = dtime->currentDateTimeUtc().toString("yyyyMMdd").toUtf8();
QJsonObject data;
this->amz = amzdate;
//this->amz = "20210306T142037Z";
this->dstamp = datestamp;
qInfo() <<"amztime";
qInfo() << amzdate;
qInfo() <<"date";
qInfo() << datestamp;
data["email"] = "user#test.co";
data["password"] = "password";
QJsonDocument doc(data);
QByteArray strData(doc.toJson(QJsonDocument::Compact));
qInfo() << "JSON parameters";
qInfo() << strData;
const QByteArray algorithmDesignation = this->algorithmDesignation(QCryptographicHash::Sha256);
const QString endpoint = this->endpoint;
QString signa = "host;x-amz-date";;
const QByteArray credentialScope = this->credentialScope(datetime, this->region, this->service);
QString access = "ACCESSKEYID/";
this->scope = access.toUtf8() + this->dstamp+ "/us-east-1/execute-api/aws4_request";
qInfo() << "credentialScope";
qInfo() << credentialScope;
QByteArray signedHeaders = signa.toUtf8();
qInfo() << "signedHeaders";
qInfo() << signedHeaders;
const QUrl URL("https://asdasd.execute-api.us-east-1.amazonaws.com/master/users/login");
const QNetworkRequest request(URL);
const QByteArray canonicalRequest = this->canonicalRequest(QNetworkAccessManager::Operation::PostOperation, request, strData, &signedHeaders);
qInfo() << "canonical request";
qInfo() << canonicalRequest;
const QByteArray stringToSign = this->stringToSign(algorithmDesignation, this->amz, credentialScope, canonicalRequest);
qInfo() << "stringToSign";
qInfo() << stringToSign;
const QByteArray signingKey = this->signingKey(datetime.date(), this->region, this->service);
qInfo() << "signinKey";
qInfo() << signingKey;
const QByteArray signature = QMessageAuthenticationCode::hash(stringToSign, signingKey, QCryptographicHash::Sha256);
qInfo() << "signature";
qInfo() << signature;
this->hashvalue = algorithmDesignation + " Credential=" + this->key.toUtf8() + '/' + credentialScope +
", SignedHeaders=" + signedHeaders + ", Signature=" + signature.toHex();
qInfo() << "AWS SIGNATURE VERSION 4";
qInfo() << this->hashvalue;
this->loginWekall(hashvalue, this->amz);
return 0;
}
CryptoHash::~CryptoHash(){};
I use wxWidgets with MVC pattern and im having trouble when i call the controller functions in the View
compareRGB(...),compareALPHA(...) and compareHSV(...)
for compare two images with a tolerance.Basically when the functions are called the program crash with code error
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
I've tried every solution but the error is still here.
In this moment the function only print a word on terminal.
This is the code that cause error(the other functions work perfectly):
void View::compareImages(wxCommandEvent &event){
int imagesActive = imagesActivatedCount();
wxString mode = getMode();
double tolerance = colorToleranceSlider->GetValue();
if(imagesActive < 2){
wxMessageBox(_("SELEZIONARE DUE IMMAGINI DA COMPARARE"),_("ERRORE"),wxOK | wxICON_EXCLAMATION);
}
else if(mode.IsSameAs(_("RGB"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareRGB(pathImage1,pathImage2,tolerance);
}
else if(mode.IsSameAs(_("HSV"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareHSV(pathImage1,pathImage2,tolerance);
}
else if(mode.IsSameAs(_("ALPHA"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareAlpha(pathImage1,pathImage2,tolerance);
}
else{
wxMessageBox(_("SCEGLIERE METODO DI COMPARAZIONE"),_("ERRORE"),wxOK | wxICON_EXCLAMATION);
}
}
The complete source:
#include "View.h"
enum{
PANEL_ID,
BUTTON_ADD,
BUTTON_REMOVE,
BUTTON_ACTIVATE,
BUTTON_COMPARE,
SLIDER_COLOR,
MODE_SELECTOR,
LIST_ID,
ABOUT,
STATIC_ID,
VALUE_SLIDER
};
/*Event table of the view*/
wxBEGIN_EVENT_TABLE(View,wxFrame)
EVT_BUTTON(BUTTON_ADD,View::loadImages)
EVT_BUTTON(BUTTON_REMOVE,View::removeImages)
EVT_BUTTON(BUTTON_ACTIVATE,View::activateSelectedImages)
EVT_BUTTON(BUTTON_COMPARE,View::compareImages)
EVT_BUTTON(BUTTON_ACTIVATE,View::activateSelectedImages)
EVT_MENU(ABOUT,View::onAbout)
EVT_MENU(wxID_EXIT,View::onExit)
EVT_SLIDER(SLIDER_COLOR,View::onSliderUpdate)
wxEND_EVENT_TABLE()
View::View(const std::string title, const wxPoint &pos, const wxSize &size, AbstractModel& model, AbstractController& c):model(model),controller(c) ,wxFrame(NULL,wxID_ANY,title,pos,size) {
model.registerObserver(this); //registration view for successive notification
//View implementation
}
void View::update(int eventCode) {
}
/*Delete the selected images
* the while cicle it afflict only the image selected*/
void View::removeImages(wxCommandEvent& event){
long item;
while((item = list->GetNextItem(-1,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED)) != -1){
list->DeleteItem(item);
}
}
/*load images on storage
* It load the paths in the list view but in the model there is a map that associate
* the wxImage object with his relative path*/
void View::loadImages(wxCommandEvent& event){
wxFileDialog* fileDialog=new wxFileDialog(this, _("Scegli una o piĆ¹ foto"),wxEmptyString,wxEmptyString,wxFileSelectorDefaultWildcardStr,wxFD_MULTIPLE);
if(fileDialog->ShowModal() == wxID_CANCEL){
return;
}
wxArrayString paths;
fileDialog->GetPaths(paths);
try {
for (auto iterator = paths.begin(); iterator < paths.end(); ++iterator) {
list->InsertItem(0,*iterator);
}
}
catch (std::exception& error) {
std::cout << "Caught exception: " << error.what() << std::endl;
}
}
int View::imagesActivatedCount() {
int count = 0;
long item = -1;
while((item = list->GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_DONTCARE)) != -1){
if (list->GetItemText(item,1).IsSameAs(_("*")))
{
count++;
}
}
return count;
}
/*Deselect the activated images
* this function is called on activatedSelectImages function for deselect before activate the new images
* For practical reasons the function scan every row in the list and if in the column next to him is present the "*"
* symbol it deselect him and clear the activated images array
*/
void View::deselectImages() {
long item = -1;
while((item = list->GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_DONTCARE)) != -1){
if(list->GetItemText(item,1).IsSameAs(_("*"))){
list->SetItem(item,1,_(""));
}
}
}
void View::activateSelectedImages(wxCommandEvent& event) {
int imageActive = imagesActivatedCount();
int itemCount=0;
long item = -1;
wxString arr[2];
if(imageActive >= 2 || list->GetSelectedItemCount() >= 2)
{
deselectImages();
imageActive = 0;
}
while((item = list->GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_SELECTED)) != -1){
if(itemCount < 2){
list->SetItem(item,1,_("*"));
itemCount++;
imageActive++;
}
}
if (imageActive == 2)
{
itemCount = 0;
item = -1;
wxString text;
while((item = list->GetNextItem(item,wxLIST_NEXT_ALL,wxLIST_STATE_DONTCARE)) != -1){
text = list->GetItemText(item,1);
if (text.IsSameAs(_("*"))){
arr[itemCount]=list->GetItemText(item);
itemCount++;
}
}
activeImages = arr;
std::cout<< *activeImages << std::endl;
std::cout << *(activeImages + 1) << std::endl;
}
}
void View::compareImages(wxCommandEvent &event){
int imagesActive = imagesActivatedCount();
wxString mode = getMode();
double tolerance = colorToleranceSlider->GetValue();
if(imagesActive < 2){
wxMessageBox(_("SELEZIONARE DUE IMMAGINI DA COMPARARE"),_("ERRORE"),wxOK | wxICON_EXCLAMATION);
}
else if(mode.IsSameAs(_("RGB"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareRGB(pathImage1,pathImage2,tolerance);
}
else if(mode.IsSameAs(_("HSV"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareHSV(pathImage1,pathImage2,tolerance);
}
else if(mode.IsSameAs(_("ALPHA"))){
wxString pathImage1 = *activeImages;
wxString pathImage2 = *(activeImages + 1);
controller.compareAlpha(pathImage1,pathImage2,tolerance);
}
else{
wxMessageBox(_("SCEGLIERE METODO DI COMPARAZIONE"),_("ERRORE"),wxOK | wxICON_EXCLAMATION);
}
}
}
The controller source:
#include "Controller.h"
Controller::Controller(AbstractModel& model) : model(model){}
void Controller::removeImages(wxArrayString paths) {
for(int it = 0; it < paths.size();it++){
model.removeImage(paths[it]);
}
}
void Controller::loadImages(wxArrayString paths) {
for(int it = 0; it < paths.size(); it++){
model.loadImage(paths[it]);
}
}
void Controller::compareRGB(wxString path1, wxString path2, double tolerance) {
std::cout<<"A"<<std::endl;
}
void Controller::compareAlpha(wxString path1, wxString path2, double tolerance) {
std::cout<<"B"<<std::endl;
}
void Controller::compareHSV(wxString path1, wxString path2, double tolerance) {
std::cout<<"C"<<std::endl;
}
As you mention in the comment,
activeImages is a wxString*
So there you have it: activeImages = arr. The latter is a local variable which will be freed at the end of activateSelectedImages(). When using activeImages later in compareImages(), you are getting undefined behavior.
I'm trying to find a solution that in case that a validation is not successful because there is more then one error in the XML file the Qt MessageHandler(line, column, description etc. ) is able to show every error in the XML data not just the first one that occurs in the XML file.
Example:
I have an error in line: 65 (see pic)
but there are also errors in line :78,83,95 but it dose not show it only shows the first one.
Is there a solution for this case? And if yes how?
My Code looks like this:
MessageHandler messageHandler;
QFile xsdfile("....xsd");
xsdfile.open(QIODevice::ReadOnly);
QXmlSchema schema;
schema.setMessageHandler(&messageHandler);
bool errorOccurred = false;
if (schema.load(&xsdfile, QUrl::fromLocalFile(xsdfile.fileName())) == false)
errorOccurred = true;
else
{
QXmlSchemaValidator xmlvalidator(schema);
QFile xmlfile("......xml");
xmlfile.open(QIODevice::ReadOnly);
if (!xmlvalidator.validate(&xmlfile, QUrl::fromLocalFile(xmlfile.fileName())))
errorOccurred = true;
xmlfile.close();
}
xsdfile.close();
if (errorOccurred) {
QString qs = messageHandler.statusMessage();
cout << "Line: " << messageHandler.line() << "\n" << "Row: " << messageHandler.column() << "\n" << "ErrorMessage: ";
std::cout << qs.toUtf8().constData() << std::endl;
return -1;
}
else {
return 0;
}
And my MessageHandler class looks like this:
class MessageHandler : public QAbstractMessageHandler
{
public:
MessageHandler()
: QAbstractMessageHandler(0)
{
}
QString statusMessage() const
{
return m_description;
}
int line() const
{
return m_sourceLocation.line();
}
int column() const
{
return m_sourceLocation.column();
}
protected:
virtual void handleMessage(QtMsgType type, const QString &description,
const QUrl &identifier, const QSourceLocation &sourceLocation)
{
Q_UNUSED(type);
Q_UNUSED(identifier);
m_description = description;
m_sourceLocation = sourceLocation;
}
private:
QString m_description;
QSourceLocation m_sourceLocation;
};
Thanks :)
I think you need to write a custom validator to catch all errors. You can modify this example code to catch all errors.
struct NodeProperties
{
NodeProperties()
{
values.resize(names.size());
}
std::vector<int> values;
static QStringList names;
static QString rootName;
static QString childName;
};
QString NodeProperties::rootName = "static_route";
QString NodeProperties::childName = "molenet";
QStringList NodeProperties::names = QList<QString>() << "node_id"
<< "nextHop_id"
<< "sink_id"
<< "delay_before_send"
<< "delay_before_sleep";
QList<NodeProperties> nodesProperties;
bool parseXMLFile()
{
QXmlStreamReader xmlStreamReader;
QString xmlFile;
xmlFile = QFileDialog::getOpenFileName(this,
tr("Open XML file"), QDir::currentPath(), tr("XML (*.xml)"));
if(xmlFile.isEmpty())
return false;
QFile file(xmlFile);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
QMessageBox::warning(this,tr("Error: Cannot read file ..."),
(xmlFile+" " + file.errorString() ));
return false;
}
nodesProperties.clear();
xmlStreamReader.setDevice(&file);
xmlStreamReader.readNext();
while(!xmlStreamReader.atEnd())
{
if(xmlStreamReader.isStartElement())
{
if(xmlStreamReader.name() == NodeProperties::rootName)
xmlStreamReader.readNext();
if(xmlStreamReader.name() == NodeProperties::childName)
{
xmlStreamReader.readNext();
if(xmlStreamReader.isCharacters() )
xmlStreamReader.readNext();
if(xmlStreamReader.isStartElement())
{
NodeProperties prop;
for(int i = 0; i < NodeProperties::names.size(); ++i)
{
if(xmlStreamReader.isCharacters() )
{
xmlStreamReader.readNext();
--i;
continue;
}
if(xmlStreamReader.name() == NodeProperties::names[i])
{
prop.values[i] = xmlStreamReader.readElementText().toInt();
}
xmlStreamReader.readNext();
}
nodesProperties.push_back(prop);
}
}
}
xmlStreamReader.readNext();
}
file.close();
if (xmlStreamReader.hasError())
{
QMessageBox::warning(this,tr("Error: Failed to parse file "),
(xmlFile+" " + xmlStreamReader.errorString() ));
nodesProperties.pop_back();
}
else if (file.error() != QFile::NoError)
{
qDebug() << "Error: Cannot read file " << qPrintable(xmlFile)
<< ": " << qPrintable(file.errorString());
}
//createGraphVizFile(xmlFile);
this->xmlFile = xmlFile;
return true;
}
The XML file is:
<?xml version="1.0"?>
<static_route>
<molenet>
<node_id> 1 </node_id>
<nextHop_id> 2 </nextHop_id>
<sink_id> 254 </sink_id>
<delay_before_send> 2 </delay_before_send>
<delay_before_sleep> 30 </delay_before_sleep>
</molenet>
<molenet>
<node_id> 2 </node_id>
<nextHop_id> 3 </nextHop_id>
<sink_id> 254 </sink_id>
<delay_before_send> 4 </delay_before_send>
<delay_before_sleep> 30 </delay_before_sleep>
</molenet>
</static_route>
I wrote a shared memory ringbuffer with QT and I turns out it works great in a single process with one consumer and one writer. If I try to read data from a second process I the first one which connected crashes with a seg fault. Maybe I'm overlooking something?
The Header File
#ifndef SHAREDMEMORYRINGBUFFER_H
#define SHAREDMEMORYRINGBUFFER_H
#include <QObject>
#include <QSharedMemory>
class SharedMemoryRingBuffer : public QObject
{
Q_OBJECT
public:
struct SharedMemoryAttributes {
int32_t readPosition;
int32_t writePosition;
int32_t size;
int32_t* data;
};
explicit SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent = 0);
~SharedMemoryRingBuffer();
int write(int32_t frame);
int32_t read();
int32_t length();
int32_t readPosition();
int32_t writePosition();
int avaibleSize();
int isEmpty();
void empty();
int isFull();
private:
SharedMemoryAttributes *_attributes;
int32_t _writePosition;
int32_t _readPosition;
int32_t _length;
QSharedMemory *_sharedMemory;
int _headSize;
signals:
void readFrame(QString name);
public slots:
};
#endif // SHAREDMEMORYRINGBUFFER_H
And the CPP File:
#include "sharedmemoryringbuffer.h"
#include <QDebug>
#define DEBUGGINGNAME "[SharedMemoryRingbuffer]"
SharedMemoryRingBuffer::SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent) : QObject(parent)
{
int sizeOfData = sizeof(int32_t) * (size);
int sizeOfHeader = sizeof(SharedMemoryAttributes);
_sharedMemory = new QSharedMemory(sharedMemoryName);
if (!_sharedMemory->attach()) {
if (!_sharedMemory->create(sizeOfData+sizeOfHeader, QSharedMemory::ReadWrite )) {
qDebug() << DEBUGGINGNAME << "Could not create shared memory object, aborting ...";
qDebug() << DEBUGGINGNAME << _sharedMemory->errorString();
//TODO: exit call
return;
}
}
_sharedMemory->lock();
_attributes = reinterpret_cast<SharedMemoryAttributes*>(_sharedMemory->data());
_attributes->readPosition = 0;
_attributes->writePosition = 0;
_attributes->size = size + 1;
_attributes->data = (int32_t*) _sharedMemory->data() + sizeOfHeader;
_sharedMemory->unlock();
}
SharedMemoryRingBuffer::~SharedMemoryRingBuffer()
{
_sharedMemory->detach();
delete _sharedMemory;
}
int SharedMemoryRingBuffer::write(int32_t frame)
{
_sharedMemory->lock();
if ( !isFull() ) {
_attributes->data[writePosition()] = frame;
if (writePosition() + 1 >= _attributes->size) _attributes->writePosition = 0;
else _attributes->writePosition += 1;
_sharedMemory->unlock();
return 1;
}
_sharedMemory->unlock();
return 0;
}
/**
* #brief SharedMemoryRingBuffer::read
* #return
* if the buffer is empty this functions returns the last readable value
*
*/
int32_t SharedMemoryRingBuffer::read()
{
_sharedMemory->lock();
int32_t frame = _attributes->data[readPosition()];
if ( readPosition() != writePosition() ) {
if (readPosition() + 1 >= _attributes->size ) _attributes->readPosition = 0;
else _attributes->readPosition += 1;
emit readFrame(_sharedMemory->key());
}
_sharedMemory->unlock();
return frame;
}
int32_t SharedMemoryRingBuffer::length()
{
if(readPosition() <= writePosition()) {
return writePosition() - readPosition();
} else {
return readPosition() - writePosition();
}
}
int32_t SharedMemoryRingBuffer::readPosition()
{
return _attributes->readPosition;
}
int32_t SharedMemoryRingBuffer::writePosition()
{
return _attributes->writePosition;
}
int SharedMemoryRingBuffer::avaibleSize()
{
return -1;
}
int SharedMemoryRingBuffer::isEmpty()
{
if ( readPosition() == writePosition() ) return 1;
else return 0;
}
void SharedMemoryRingBuffer::empty()
{
}
int SharedMemoryRingBuffer::isFull()
{
if ((writePosition() + 1) % _attributes->size == readPosition()) return 1;
else return 0;
}
This initialization:
_attributes->size = size + 1;
looks incorrect since the size you pass to _sharedMemory->create() is only allocating enough "data" space for size elements, not size + 1. Why is the +1 there?
So I found a solution which was pretty obvious. If a second process connects to to the shared memory it should NOT set the head and tail position to 0.
This is the fixed code, which works well :)
#include "sharedmemoryringbuffer.h"
#include <QDebug>
#define DEBUGGINGNAME "[SharedMemoryRingbuffer]"
SharedMemoryRingBuffer::SharedMemoryRingBuffer(QString sharedMemoryName, int32_t size, QObject *parent) : QObject(parent)
{
int sizeOfData = sizeof(int32_t) * (size);
int sizeOfHeader = sizeof(SharedMemoryAttributes);
_sharedMemory = new QSharedMemory(sharedMemoryName);
if (_sharedMemory->isAttached()) _sharedMemory->detach();
if (!_sharedMemory->attach()) {
_sharedMemory->lock();
_attributes = reinterpret_cast<SharedMemoryAttributes*>(_sharedMemory->data());
_attributes->readPosition = 0;
_attributes->writePosition = 0;
_attributes->size = size;
_sharedMemory->unlock();
if (!_sharedMemory->create(sizeOfData+sizeOfHeader, QSharedMemory::ReadWrite )) {
qDebug() << DEBUGGINGNAME << "Could not create shared memory object, aborting ...";
qDebug() << DEBUGGINGNAME << _sharedMemory->errorString();
//TODO: exit call
return;
}
}
_attributes->data = (int32_t*) _sharedMemory->data() + sizeOfHeader;
}
SharedMemoryRingBuffer::~SharedMemoryRingBuffer()
{
_sharedMemory->detach();
delete _sharedMemory;
}
int SharedMemoryRingBuffer::write(int32_t frame)
{
_sharedMemory->lock();
if ( !isFull() ) {
_attributes->data[writePosition()] = frame;
if (writePosition() + 1 >= _attributes->size) _attributes->writePosition = 0;
else _attributes->writePosition += 1;
_sharedMemory->unlock();
return 1;
}
_sharedMemory->unlock();
return 0;
}
/**
* #brief SharedMemoryRingBuffer::read
* #return
* if the buffer is empty this functions return the last readable value
*
*/
int32_t SharedMemoryRingBuffer::read()
{
_sharedMemory->lock();
int32_t frame = _attributes->data[readPosition()];
if ( readPosition() != writePosition() ) {
if (readPosition() + 1 >= _attributes->size ) _attributes->readPosition = 0;
else _attributes->readPosition += 1;
emit readFrame(_sharedMemory->key());
}
_sharedMemory->unlock();
return frame;
}
int32_t SharedMemoryRingBuffer::length()
{
if(readPosition() <= writePosition()) {
return writePosition() - readPosition();
} else {
return readPosition() - writePosition();
}
}
int32_t SharedMemoryRingBuffer::readPosition()
{
return _attributes->readPosition;
}
int32_t SharedMemoryRingBuffer::writePosition()
{
return _attributes->writePosition;
}
int SharedMemoryRingBuffer::avaibleSize()
{
return -1;
}
int SharedMemoryRingBuffer::isEmpty()
{
if ( readPosition() == writePosition() ) return 1;
else return 0;
}
void SharedMemoryRingBuffer::empty()
{
}
int SharedMemoryRingBuffer::isFull()
{
if ((writePosition() + 1) % _attributes->size == readPosition()) return 1;
else return 0;
}
I am attempting to build my own custom QAbstractNetworkCache implementation for use with QNetworkAccessManager.
I am having trouble with QAbstractNetworkCache::insert(QIODevice *device); inside this method, the device always arrives with 0 bytes to read from.
As I understand, the QIODevice* that is returned from QAbstractNetworkCache::prepare(const QNetworkCacheMetaData &metaData) is going to be filled with data and used as a parameter to QAbstractNetworkCache::insert(QIODevice *device) method once QNetworkAccessManager finishes downloading.
So I have prepared a QBuffer to be this container, but whenever QAbstractNetworkCache::insert(QIODevice *device) is getting called, it always arrives with nothing in it (device->bytesAvailable() == 0)
QIODevice* NetworkCachePrivate::prepare(const QNetworkCacheMetaData &metaData) {
if (!metaData.isValid() || !metaData.url().isValid() || cacheDir.isEmpty()) return 0;
QIODevice* device = 0;
QString hash = hexMD5(metaData.url().toString());
QScopedPointer<QBuffer> buffer(new QBuffer);
if (buffer->open(QIODevice::ReadWrite))
{
qDebug() << "BUFFER READY";
device = buffer.take();
deviceMapping[device] = qMakePair(hash, metaData);
}
return device;
}
void NetworkCachePrivate::insert(QIODevice *device) {
if (deviceMapping.contains(device))
{
QPair<QString, QNetworkCacheMetaData> pair = deviceMapping[device];
QString fileName;
fileName += cacheDir;
fileName += QLatin1String("/");
fileName += pair.first;
qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //ALWAYS 0!!!! :(
QFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
qint64 size = file.write(device->readAll());
if (size <= 0)
{
file.remove();
}
else
{
qDebug() << "FILE WROTE " << size;
cacheSize += size;
}
}
deviceMapping.remove(device);
delete device;
}
}
QNetworkCacheMetaData NetworkCachePrivate::metaData (const QUrl &url ) {
QString fileName;
fileName += cacheDir;
fileName += QLatin1String("/");
fileName += hexMD5(url.toString());
QNetworkCacheMetaData data;
if (!QFile::exists(fileName))
return data;
data.setUrl(url);
data.setExpirationDate(QDateTime::currentDateTime().addYears(1));
return data;
}
As peppe also pointed out in the comment, you need to seek the QBuffer to the beginning after the write and before the read as per the documentation:
QBuffer allows you to access a QByteArray using the QIODevice interface. The QByteArray is treated just as a standard random-accessed file. Example:
QBuffer buffer;
char ch;
buffer.open(QBuffer::ReadWrite);
buffer.write("Qt rocks!");
*** buffer.seek(0); ***
buffer.getChar(&ch); // ch == 'Q'
buffer.getChar(&ch); // ch == 't'
buffer.getChar(&ch); // ch == ' '
buffer.getChar(&ch); // ch == 'r'
To be more concrete for your code, you would need to insert the statement like this:
...
device.seek(0);
qDebug() << "DEVICE BYTES" << device->bytesAvailable(); //NOT 0 ANYMORE!!!! :)
...