I want to write a simple function to swap bytes in a QByteArray. This is what I have come up with:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString t = "abcde";
QByteArray test;
test.append(t.at(0));
test.append(t.at(1));
test.append(t.at(2));
test.append(t.at(3));
test.append(t.at(4));
qDebug() << t;
qDebug() << QString(swapBytes(test,0,3));
return a.exec();
}
QByteArray swapBytes(QByteArray in, int swapOffset, int quantity) {
if (swapOffset < 0) {
return in;
}
if(quantity>(in.length()/2)) {quantity=in.length()/2;}
if(quantity < 1) {quantity=1;}
int k;
char buf[quantity];
char buf2[quantity];
qDebug() << quantity;
for (int i = 0; i + quantity*2 + swapOffset <= in.length(); i=i+2*quantity) {
k=i;
for(int b = 0;b<i+quantity;b++){
buf[b]=in.at(k);
buf2[b]=in.at(k+swapOffset+quantity);
k++;
}
qDebug() << buf;
qDebug() << buf2;
qDebug() << in;
in.replace(0,quantity,buf2);
qDebug() << in;
in.replace(quantity+swapOffset,quantity,buf);
}
return in;
}
For some reason when I run this code I get the following output :
abcde
ab(
cd
abcde
cdcde
cdab(e
Where does the parentheses come from? As far as I know there is only one char per byte, so what is wrong?
You need to allocate one byte more than quantity to leave room for the null terminator. Try this:
char* buf = new char[quantity+1];
char* buf2 = new char[quantity+1];
memset(buf, 0, quantity+1);
memset(buf2, 0, quantity+2);
Then before your function returns, remember to deallocate:
delete [] buf;
delete [] buf2;
return in;
Alternately, you can just use a QByteArray in place of a char array:
QByteArray buf(quantity, 0x00);
QByteArray buf2(quantity, 0x00);
This allows you to skip calls to memset and to avoid worrying about deallocation.
Unrelated to all of this, note that you can initialize a QByteArray from a QString like this:
QByteArray test = t.toAscii();
Related
I'm running into an annoying issue where I cannot access memory obtained from mmap in any way without getting a segmentation fault.
The function I used to obtain the mapped memory looks like this.
/**
* Preconditions: filename must be verified as referencing a valid file.
*/
char *IOUtils::memory_map_file(string const& filename, size_t length, int open_flags){
int fd = open(filename.c_str(), open_flags | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int prot;
if (open_flags == O_RDONLY)
prot = PROT_READ;
else
prot = PROT_READ | PROT_WRITE;
void *output = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (output == (void *) -1){
cerr << filename << ": " << strerror(errno) << '\n';
_exit(2);
}
close(fd);
return (char *)output;
}
My main function looks like this.
int main(int argc, char *argv[]){
size_t input_length = IOUtils::file_size(argv[1]); //This works fine
char *input_buffer = IOUtils::memory_map_file(argv[1], input_length,
O_RDONLY); //This succeeds
char *output_buffer = IOUtils::memory_map_file(argv[2], 2*input_length,
O_RDWR); //This succeeds
DomainParser parser(input_length, input_buffer, output_buffer);
while(!parser.finished()){
parser.write_entry();
}
mremap(output_buffer, 2*input_length, MREMAP_MAYMOVE,
parser.bytes_written());
munmap(output_buffer, parser.bytes_written());
}
The parser's relevant code looks like this
void DomainParser::write_entry(void){
char const *in = input(); //Gets position in input file
char const *copy_up_to = end(); //Gets position at input EOF
for(char const *it = in; it < copy_up_to; ++it){
cerr << *it; //SIGSEGV!
if(*it == '\n') break;
}
cerr << '\n';
/* Do writes */
}
The program segfaults immediately upon cerr << *it. I have no idea why this would happen, considering all mapped memory is equipped with read permissions and is successfully allocated.
Edit: If anyone suspects the class is broken somewhere, here's the full source code.
using std::stringstream;
using std::string;
class DomainParser{
size_t _input_offset;
const size_t _input_length;
size_t _output_offset;
char const *input_buffer;
char *output_buffer;
char const *input(void){
return input_buffer + _input_offset;
}
char *output(void){
return output_buffer + _output_offset;
}
char const* end(void){
return input_buffer + _input_length;
}
char const *find(char const *begin, char const *max, char c){
while (*begin != c){
cerr << *begin++;
}
cerr << c;
return begin;
}
public:
DomainParser(size_t length, char const *input, char *output) :
_input_length(length), input_buffer(input), output_buffer(output)
{}
bool finished(void){
return _input_offset == _input_length;
}
size_t bytes_written(void){
return _output_offset;
}
size_t write_entry(void){
if (finished()){
return 0;
}
char const *in = input();
char const *copy_up_to = find(in, end(), '\n');
size_t input_entry_length = copy_up_to - in;
string s(in, copy_up_to);
stringstream ss(s);
string name, type, host;
ss >> name >> type >> host;
if (!ss){
cerr << s << '\n';
_input_offset += input_entry_length;
return 0;
}
ss.str(""); ss.clear();
ss << "{\"name\":\"" << name << "\"," <<
"\"host\":\"" << host << "\"," <<
"\"type\":\"" << type << "\"}\n";
string entry = ss.str();
std::memcpy(output(), entry.c_str(), entry.size());
_input_offset += input_entry_length;
_output_offset += entry.size();
return entry.size();
}
};
I don't see any initialization of _input_offset.
If you fix that, you will run into the problem that the output file is empty, so accessing any pages will trigger a SIGBUS signal. You need to resize it using ftruncate to the intended size (probably to match the size of the mapping, but this depends on what you are trying to do).
Also not that munmap can be very expensive (especially on large systems), so memory-mapped I/O is only a win when the file sizes are quite large.
I've been reading many suggestions on the same topic, and tried to implement many of them, but it seems that none of them is actually working in my environment.
I'm using QT 5, but I think the problem is not related to QT but to how the hexadecimal character 0x00 is interpreted by the language.
What I have to achieve is to display a stream of unsigned char as hexadecimal values, eg:
Input bytes: 0x00 0x4E 0x01 0x00 0x17 0x00
Display as: 0x00:0x4E:0x01:0x00:0x17:0x00
it seems quite easy, but all I get is an empty string...
The functions I wrote:
QString getBufferAsHexStr(const unsigned char* buf, int buffsize) {
std::string finalstring("");
char tempbuff[5];
int n=0, index=0;
for (int c = 0; c < buffsize; c++) {
if(c == buffsize-1) {
n=sprintf(tempbuff, "0x%02X", buf[c]);
} else {
n=sprintf(tempbuff, "0x%02X:", buf[c]);
}
finalstring.append(tempbuff, n);
index += n;
}
QString resultStr(finalstring.c_str());
return resultStr;
}
QString getBufferAsHexStr(const unsigned char* buf, int buffsize) {
std::stringstream ss;
for (int c = 0; c < buffsize; c++) {
if(c == buffsize-1) {
ss << std::hex << std::showbase << buf[c];
} else {
ss << std::hex << std::showbase << buf[c] << ":";
}
}
const std::string finalstring = ss.str();
QString resultStr(finalstring.c_str());
return resultStr;
}
I don't know why you started to use C++ functions with C++ types when you have a much better alternative which is QString. Using QString you might implement it as follows:
QString getBufferAsHexStr(const unsigned char* buf, int buffsize) {
QString result;
for(int i = 0; i < buffsize; ++i)
result += "0x" + QString("%1:").arg(buf[i], 2, 16, QChar('0')).toUpper();
result.chop(1);
return result;
}
Another version using a QByteArray and a joined QStringList:
QString getBufferAsHexStr(QByteArray buf) {
QStringList byteStrings;
for (int i = 0; i < buf.size(); ++i)
byteStrings += QString("0x") + QString("%1").arg(buf[i], 2, 16, QChar('0')).toUpper();
return byteStrings.join(":");
}
It would by called using
QString result = getBufferAsHexStr(QByteArray(charArr, charArrSize));
I tried the following:
Write
int a[5]={1,2,3,4,5};
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
out << a[5];
int size = buffer.size();
if (!sharedMemory.create(size)) {
ui.label->setText(tr("Unable to create shared memory segment."));
return;
}
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(sharedMemory.size(), size));
sharedMemory.unlock();
Read
sharedMemory.attach();
QBuffer buffer;
QDataStream in(&buffer);
int a[5];
//QImage image;
QString fileName;
int num;
sharedMemory.lock();
buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
buffer.open(QBuffer::ReadOnly);
in >> a[5];
sharedMemory.unlock();
But the way I thought was wrong.I do not know how to read and write is an array of characters and integers .
The first real problem here is a[5]: the array a only has 5 items, so the 5th index is out of its bounds. To write and read the whole array you can 1) do it one item at a time; or 2) with a vector or list object such as QVector.
1. As Array
Write:
int a[5]={1,2,3,4,5};
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
//write item by item
for (int i=0; i<5; i++) {
out << a[i];
}
int size = buffer.size();
if (!sharedMemory.create(size)) {
cout << "Unable to create shared memory segment: "<<sharedMemory.errorString().toStdString()<<endl;
return;
}
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(sharedMemory.size(), size));
sharedMemory.unlock();
Read:
sharedMemory.attach();
QBuffer buffer;
QDataStream in(&buffer);
sharedMemory.lock();
buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
buffer.open(QBuffer::ReadOnly);
int len = sharedMemory.size()/sizeof(int);
int* a = new int[len];
for (int i=0; i< len; i++) {
in >> a[i];
}
sharedMemory.unlock();
//delete a;
(Note: don't forget to delete a after usage.)
2. As QVector
Write:
QVector<int> vec;
vec << 1<<2<<3<<4<<5;
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
//write the vector object
out << vec;
int size = buffer.size();
if (!sharedMemory.create(size)) {
cout << "Unable to create shared memory segment: "<<sharedMemory.errorString().toStdString()<<endl;
return;
}
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(sharedMemory.size(), size));
sharedMemory.unlock();
Read:
sharedMemory.attach();
QBuffer buffer;
QDataStream in(&buffer);
sharedMemory.lock();
buffer.setData((char*)sharedMemory.constData(), sharedMemory.size());
buffer.open(QBuffer::ReadOnly);
QVector<int> vec;
in>>vec;
sharedMemory.unlock();
the best way to keep a pointer in main changes reflected inside the class?
static unsigned char tmp[][20] = {"hello world", "bye world"};
class X {
unsigned char ** buffer;
public:
X(unsigned char* buff)
{
buffer = &buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
};
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(buff);
x.printThis();
memcpy(buff, tmp[1], 12);
x.printThis();
delete [] buff;
return EXIT_SUCCESS;
}
this works, but when I do the follow
buff = tmp[0];
x.printThis();
the printout doesnt print hello world again??? how to fix that
You'll need to use a pointer to pointer in your class (gulp!):
class X {
unsigned char ** buffer;
public:
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
};
And then pass in the address of the pointer during construction:
X x(&buff);
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(buff);
x.printThis();
delete [] buff;
buff = tmp[1];
x.printThis();
return EXIT_SUCCESS;
}
After you have done delete buff;, your pointer buffer in the class is pointing at memory that has been deleted, which is very bad news.
If you want to store the actual address of buff, you would need to pass the address of buff and store that, like this:
char **buffer;
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
...
X x(&buff);
Or you could make buffer a reference to buff:
char*& buffer;
X(unsigned char*& buff) : buffer(buff) {}
(No other changes needed in class or other code - but note that you can't do buffer = some_other_buffer; at a later stage - that will change the value of buff to some_other_buffer, which is probably not what you expected).
You can do something thing as bellow (using a pointer to a pointer), but sincerally, this more a problem than a solution because you are unable to delete tmp without a good care with the pointer in class X
#include <cstdio>
#include <cstring>
static unsigned char tmp[][20] = {"hello world", "bye world"};
class X {
unsigned char ** buffer;
public:
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
printf(".......> %s", *buffer);
}
};
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(&buff);
x.printThis();
buff = NULL;
buff = tmp[1];
x.printThis();
}
When we create a text file with this text "ali ata bak", and we use this file as input for the program. The code is running normally. But when we enter "1111111111111111111111" this text in the textfile, Code isnt running expected. So What is the problem?
#include <QtCore/QCoreApplication>
#include <QBitArray>
#include <QByteRef>
#include <QFile>
#include <iostream>
#include <stdlib.h>
#include <QTextStream>
// Buffer Size #num of KB's
#define BUFFER_SIZE_KB 1
// Do not change !!
#define BUFFER_SIZE_BYTE BUFFER_SIZE_KB*1024
#define BUFFER_SIZE_BIT BUFFER_SIZE_BYTE*8
using namespace std;
QBitArray bytesToBits(QByteArray bytes) {
QBitArray bits(bytes.count()*8);
// Convert from QByteArray to QBitArray
for(int i=0; i<bytes.count(); ++i)
for(int b=0; b<8; ++b)
bits.setBit(i*8+b, bytes.at(i)&(1<<b));
return bits;
}
QByteArray bitsToBytes(QBitArray bits) {
QByteArray bytes;
bytes.resize(bits.count()/8);
// Convert from QBitArray to QByteArray
for(int b=0; b<bits.count(); ++b)
bytes[b/8] = ( bytes.at(b/8) | ((bits[b]?1:0)<<(b%8)));
return bytes;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString inFilename;
QString outFilename;
QTextStream qtin(stdin);
cout << "Filename : ";
qtin >> inFilename;
outFilename.append("_");
outFilename.append(inFilename);
QFile infile(inFilename);
if (!infile.open(QIODevice::ReadOnly)) {
cout << "\nFile cant opened\n";
system("pause");
return 1;
}
QFile outfile(outFilename);
if (!outfile.open(QIODevice::WriteOnly)) {
cout << "\nFile cant opened\n";
system("pause");
return 2;
}
QByteArray bytes, bytes2;
QBitArray bits;
while ((bytes = infile.read(BUFFER_SIZE_BYTE)) >0 ) {
bits = bytesToBits(bytes);
bytes2 = bitsToBytes(bits);// PROBLEM IS HERE
outfile.write(bytes2);
}
outfile.close();
infile.close();
cout << "Finished\n";
return a.exec();
}
Initialization problem.
QByteArray bitsToBytes(QBitArray bits) {
QByteArray bytes;
bytes.resize(bits.count()/8+1);
bytes.fill(0);
// Convert from QBitArray to QByteArray
for(int b=0; b<bits.count(); ++b)
bytes[b/8] = ( bytes.at(b/8) | ((bits[b]?1:0)<<(b%8)));
return bytes;
}
this produces the right answer
QByteArray bitsToBytes(const QBitArray& bits)
{
QByteArray bytes;
QDataStream stream(&bytes, QIODevice::WriteOnly);
stream << bits;
return bytes;
}
You might also want to read about endianness from the Wikipedia http://en.wikipedia.org/wiki/Endianness
QBitArray bits(8);
// The deep copy of bits
QByteArray bytes(bits.bits(), bits.count() / 8);
--------------------------------------------------------------------------
// The bits are not copied
QByteArray bytes = QByteArray::fromRawData(bits.bits(), bits.count() / 8);
Based on answers from iloahz, solve the problem of output file size grow up 1 byte.
QByteArray bitsToBytes(QBitArray bits) {
QByteArray bytes;
bytes.resize(bits.count() / 8 + ((bits.count() % 8)? 1: 0));
bytes.fill(0x00);
// Convert from QBitArray to QByteArray
for(int b = 0; b < bits.count(); ++b)
bytes[b / 8] = (bytes.at(b / 8) | ((bits[b]? 1: 0) << (b % 8)));
return bytes;
}