I have an application which consists of two primary modules. One is written in C, uses standard C runtime library and one written in Qt C++. They communicate with each other with IPC. C module creates a char array, fills it with data and sends to the module written in Qt. I want to deserialize received data using QDataStream, but my efforts didn't yield any result yet. Here's a simple example what I'm trying to achieve:
unsigned int pointer = 0;
const int IPC_MSG_LEN = 500;
const int IPC_MSG_HEADER = 200;
const int SOMETHING = 1443;
char api = 55;
char msg[IPC_MSG_LEN] = {0};
memcpy_s(msg, IPC_MSG_LEN, &IPC_MSG_HEADER, sizeof(int));
pointer = sizeof(unsigned int);
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &api, sizeof(char));
++pointer;
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &SOMETHING, sizeof(int));
QByteArray arr(msg, IPC_MSG_LEN);
QDataStream ds(&arr, QIODevice::ReadOnly);
qint32 header = 0, aa = 0;
qint8 t_api = 0;
ds >> header; //Doesn't work
ds >> t_api; //Works
ds >> aa; //Doesn't work
As you can see, the code is pretty simple, but header and aa variables are deserialized to a random number. However t_api (one byte variable) has correct value assigned.
So what's the problem with this code? Does QDataStream uses a private data format which is not compatible with the one I'm using? Should I write my own QIODevice implementation or there is a quick fix I'm not aware of? :)
Thanks, I appreciate your help.
UPDATE
Thank you very much guys, your solution worked perfectly with those primitive data types, but the problem is that I also need to be able to serialize/deserialize char* strings too.
wchar_t* name1 = L"something";
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, name1, (wcslen(name1) + 1) * 2);
char* ai = new char[500];
ds >> ai; //ai becomes NULL here :|
Is there a way to achieve that? Thanks again
QDataStream::setByteOrder(QDataStream::LittleEndian);
#include <QDebug>
#include <QByteArray>
#include <QDataStream>
#include <QString>
#include <vector>
template<typename T> void writePtr(char*&dst, T data){
*reinterpret_cast<T*>(dst) = data;
dst += sizeof(T);
}
int main(int argc, char** argv){
const size_t ipcSize = 512;
std::vector<char> buffer(ipcSize, 0);
quint32 sendVal1 = 0x12345678, recvVal1 = 0;
quint8 sendVal2 = 0xee, recvVal2 = 0;
quint32 sendVal3 = 0x9999abcd, recvVal3 = 0;
char* dst = &buffer[0];
writePtr(dst, sendVal1);
writePtr(dst, sendVal2);
writePtr(dst, sendVal3);
QByteArray byteArray(&buffer[0]);
QDataStream stream(&byteArray, QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream >> recvVal1 >> recvVal2 >> recvVal3;
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal1, 8, 16).arg (recvVal1, 8, 16);
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal2, 2, 16).arg(recvVal2, 2, 16);
qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal3, 8, 16).arg(recvVal3, 8, 16);
return 0;
}
but the problem is that I also need to be able to serialize/deserialize char* strings too.
Qt data serialization format is explained (in detail) here. You MUST read that document if you want to use QDataStream for IPC. Qt has nice documentation, so use it.
Also this is not a char* string:
wchar_t* name1 = L"something";
It is a wchar_t* string.
wchar_t has different size depending on compiler - either 4 or 2 bytes per wchar_t. Which means problem for IPC. unlike wchar_t, char is guaranteed to be 1 byte big.
So either encode entire string to UTF8 (or use 8bit-encoded string with known codepage/encoding) and write it as raw data in QByteArray-compatible format:
void writeDataPtr(char*& ptr, const char* data, quint32 size){
if (!data){
size = 0xffffffff;
writePtr(ptr, size);
return;
}
memcpy(ptr, data, size);
ptr += size;
}
Then use QString::fromUtf8 to decode it (or QTextCodec - if you decided to use other 8bit encoding instead of utf8). OR if you can ensure that your wchar_t* string is UTF16-compliant and sizeof(wchar_t) == 2, dump it in QString-compatible format.
By the way - If I were you, I'd avoid memcpy_s. It is not part of C++ standard, which is a very good reason to avoid it.
I want is to read wchar_t*/char* from QDataStream until stream position gets to null terminating character.
If this is homework, tag your post accordingly.
One of those should work:
QString readWcharString(QDataStream& stream){
QVector<ushort> rawData;
ushort tmp;
do{
stream >> tmp;
rawData.push_back(tmp)
}while(tmp);
return QString::fromUtf16(rawData.data());
}
or
QString readWcharString(QDataStream& stream){
QVector<wchar_t> rawData;
ushort tmp;
do{
stream >> tmp;
rawData.push_back(tmp)
}while(tmp);
return QString::fromWCharArray(rawData.data());
}
QDataStream stores the numbers in the big endian format by default.
You can change that with:
ds.setByteOrder(QDataStream::ByteOrder(QSysInfo::ByteOrder));
which will use the detected host endianness instead.
Related
I have a WAV file that I decode with the QAudioDecoder. As a result I have a QAudioBuffer object. I want to store the data stored in QAudioBuffer in a QByteArray for my QIODevice derived class. I want to use this data in the ReadData method of my derived class for audio output. I now have 2 questions:
How to get a QByteArray from a QAuddioBuffer?
I used the following code, but unfortunately this is not correct. The data in QAudioBuffer is coded to 2Bytes, but each element in a QByteArray is coded to 1Byte (right?). Don't we have a loss of information there? To test if QByteArray contains the original data from the WAV file, I save it to a TXT file.
is this approach appropriate? I actually want to apply some operations on the data stored in QAudioBuffer (e.g. filters) and listen to the result in real time.
Thanks in advance.
Here is the code
QAudioFormat *format_decoder;
format_decoder = new QAudioFormat;
format_decoder->setSampleRate(44100);
format_decoder->setChannelCount(1);
format_decoder->setSampleFormat(QAudioFormat::Int16);
QAudioDecoder decoder;
decoder.setSource(filenameSource);
decoder.setAudioFormat(*format_decoder);
decoder.start();
QObject::connect(&decoder, &QAudioDecoder::bufferReady, this, &MainWindow::slot_bufReady);
and the slot
void MainWindow::slot_bufReady(){
QAudioBuffer buffer = m_audioDecoder->read();
QByteArray buffer_ByteArray(buffer.constData<char>(), buffer.byteCount());
QFile file(filenameTest1);
if(!file.open(QIODevice::WriteOnly|QIODevice::Append)) {
qDebug() << "ERRO "; }
QTextStream strem(&file);
for(auto const dat: buffer_ByteArray) {
strem<< qreal(dat)/128.0<< "\r\n";
}
file.cloe();
This looks suspicious:
for(auto const dat: buffer_ByteArray) {
strem<< qreal(dat)/128.0<< "\r\n";
}
Your audio format is 16-bit mono. Reading it byte by byte is a non-starter. Read it sample by sample. That is, read two bytes at a time and convert. More likely this:
int16_t* data = (int16_t*)(buffer.data());
int samples = buffer.sampleCount();
for (int i = 0; i < samples; i++)
{
strem << data[i] << "\r\n";
}
The above will save your samples into a text file. You could plot it with Excel. But as others have said, that's not as useful as saving in as binary. You could prepend a WAV file header such that it can be played and analyzed with other tools.
Update
If your intent is to transcode from 16-bit to 8-bit, this is how you would likely do it:
int16_t* data = (int16_t*)(buffer.data());
QByteArray buffer_ByteArray(buffer.sampleCount(), '\0');
for (size_t i = 0; i < samples; i++) {
buffer_ByteArray[i] = (char)(data[i] / 256); // 16-bit to 8-bit
}
Note: some audio platforms use unsigned integers for 8-bit audio. That is the zero amplitude sample is 0x80. This is the case for 8-bit WAV files. If that's in play, then change this line:
buffer_ByteArray[i] = (char)(data[i] / 256); // 16-bit to 8-bit
To this:
char c = (char)(data[i] / 256); // 16-bit to 8-bit signed
const unsigned char mask = 0x80;
buffer_ByteArray[i] = (char)(mask ^ c);
I have a long array of char (coming from a raster file via GDAL), all composed of 0 and 1. To compact the data, I want to convert it to an array of bits (thus dividing the size by 8), 4 bytes at a time, writing the result to a different file. This is what I have come up with by now:
uint32_t bytes2bits(char b[33]) {
b[32] = 0;
return strtoul(b,0,2);
}
const char data[36] = "00000000000000000000000010000000101"; // 101 is to be ignored
char word[33];
strncpy(word,data,32);
uint32_t byte = bytes2bits(word);
printf("Data: %d\n",byte); // 128
The code is working, and the result is going to be written in a separate file. What I'd like to know is: can I do that without copying the characters to a new array?
EDIT: I'm using a const variable here just to make a minimal, reproducible example. In my program it's a char *, which is continually changing value inside a loop.
Yes, you can, as long as you can modify the source string (in your example code you can't because it is a constant, but I assume in reality you have the string in writable memory):
uint32_t bytes2bits(const char* b) {
return strtoul(b,0,2);
}
void compress (char* data) {
// You would need to make sure that the `data` argument always has
// at least 33 characters in length (the null terminator at the end
// of the original string counts)
char temp = data[32];
data[32] = 0;
uint32_t byte = bytes2bits(data);
data[32] = temp;
printf("Data: %d\n",byte); // 128
}
In this example by using char* as a buffer to store that long data there is not necessary to copy all parts into a temporary buffer to convert it to a long.
Just use a variable to step through the buffer by each 32 byte length period, but after the 32th byte there needs the 0 termination byte.
So your code would look like:
uint32_t bytes2bits(const char* b) {
return strtoul(b,0,2);
}
void compress (char* data) {
int dataLen = strlen(data);
int periodLen = 32;
char* periodStr;
char tmp;
int periodPos = periodLen+1;
uint32_t byte;
periodStr = data[0];
while(periodPos < dataLen)
{
tmp = data[periodPos];
data[periodPos] = 0;
byte = bytes2bits(periodStr);
printf("Data: %d\n",byte); // 128
data[periodPos] = tmp;
periodStr = data[periodPos];
periodPos += periodLen;
}
if(periodPos - periodLen <= dataLen)
{
byte = bytes2bits(periodStr);
printf("Data: %d\n",byte); // 128
}
}
Please than be careful to the last period, which could be smaller than 32 bytes.
const char data[36]
You are in violation of your contract with the compiler if you declare something as const and then modify it.
Generally speaking, the compiler won't let you modify it...so to even try to do so with a const declaration you'd have to cast it (but don't)
char *sneaky_ptr = (char*)data;
sneaky_ptr[0] = 'U'; /* the U is for "undefined behavior" */
See: Can we change the value of an object defined with const through pointers?
So if you wanted to do this, you'd have to be sure the data was legitimately non-const.
The right way to do this in modern C++ is by using std::string to hold your string and std::string_view to process parts of that string without copying it.
You can using string_view with that char array you have though. It's common to use it to modernize the classical null-terminated string const char*.
The response payload of my http request looks like this (but can be modified to any string best suitable for the task):
"{0X00,0X01,0XC8,0X00,0XC8,0X00,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,}"
How do I turn it into an unsigned char array containing the hex values like this:
unsigned char gImage_test[14] = { 0X00,0X01,0XC8,0X00,0XC8,0X00,
0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,}
Additional information: The length of the payload string is known in advance and always the same. Some partial solution I found can't be directly applied due to the limitations of the wrapper nature of Arduino for c++. Looking for a simple solution within the Arduino IDE.
Use sscanf("%x", ...), here an example of just 3 hex numbers:
const char *buffer = "{0X00,0X01,0XC8}";
unsigned int data[3];
int read_count = sscanf(buffer, "{%x,%x,%x}", data, data+1, data+2);
// if successful read_count will be 3
If using sscanf() (#include <stdio.h>) is within your limitations then you can call with it "%hhx" to extract each individual hex value into an unsigned char like this:
const int PAYLOAD_LENGTH = 14; // Known in advance
unsigned char gImage_test[PAYLOAD_LENGTH];
#include <stdio.h>
int main()
{
const char* bufferPtr = "{0X00,0X01,0XC8,0X00,0XC8,0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF}";
for (int i = 0; i < PAYLOAD_LENGTH && sscanf(bufferPtr + 1, "%hhx", &gImage_test[i]); i++, bufferPtr += 5);
return 0;
}
I'm think pointer is 4Byte or(2Byte) heap memory allocated
I need Structure data convert to char* Just need 12byte convert
this is my code
please Contact me.
thank you
struct MyData {
unsigned int myNumber;
float x;
float y;
};
int main(){
Mydata* mydata = new Mydata();
mydata->userNumber = 188242268;
mydata->x = 100.0f;
mydata->y = 102.0f;
char* sender = (char*)&mydata;
sioclient->send(sender);
// SocketIO Server Send
}
I don't think what you are asking is possible. string doesn't like having null characters in itself, which would be necessary to directly send the data (For example, a int(1) would have 1 NULL (0x0) character (modern systems would have 3 NULL characters) and 1 0x1 character, which string wouldn't like the null and terminate the string there).
I think you should find another networking library for what you are doing if you can't use WebSocket and need to have the efficiency of sending the bytes of a struct. (Boost::asio perhaps?)
But if you HAVE to use Cocos2d-x SIOClient without the WebSocket, then in the sending side, create a string with the values separated by commas:
string sendMe = to_string(mydata->userNumber) + "," + to_string(mydata->x) + "," + to_string(mydata->y);
And then on the receiving side:
Mydata receiver;
string recStr;//Received string
string temp;
int stage = 0;
for (int itr = 0; itr < temp.length(); itr++)
{
if (recStr[itr] == ',')
{
if (stage==0)
receiver.myNumber = stoi(temp);
else if (stage==1)
receiver.x = stof(temp);
stage++;
temp = "";
}
else
temp += recStr[itr];
}
receiver.y = stof(temp);
Hope that helps.
The old answer, in case you want to switch and have a solution:
Ok, try using the overloaded function
void send (const unsigned char * binaryMsg, unsigned int len);
instead. read about it here
This should look something like the following:
sioclient->send(reinterpret_cast<unsigned char*>(mydata), sizeof(MyData));
Tell me if you are using a different version of Cocos2d-x and thus this doesn't work, or if it failed to solve the problem. When I tried this, it compiled nicely and seemed to behave correctly.
If your compiler doesn't like unsigned char* for a const unsigned char* parameter, just add a const_cast.
Also, remember to delete (mydata); when you are done with it.
I have a struct
struct Packet {
int senderId;
int sequenceNumber;
char data[MaxDataSize];
char* Serialize() {
char *message = new char[MaxMailSize];
message[0] = senderId;
message[1] = sequenceNumber;
for (unsigned i=0;i<MaxDataSize;i++)
message[i+2] = data[i];
return message;
}
void Deserialize(char *message) {
senderId = message[0];
sequenceNumber = message[1];
for (unsigned i=0;i<MaxDataSize;i++)
data[i] = message[i+2];
}
};
I need to convert this to a char* , maximum length MaxMailSize > MaxDataSize for sending over network and then deserialize it at the other end
I can't use tpl or any other library.
Is there any way to make this better I am not that comfortable with this, or is this the best we can do.
since this is to be sent over a network, i strongly advise you to convert those data into network byte order before transmitting, and back into host byte order when receiving. this is because the byte ordering is not the same everywhere, and once your bytes are not in the right order, it may become very difficult to reverse them (depending on the programming language used on the receiving side). byte ordering functions are defined along with sockets, and are named htons(), htonl(), ntohs() and ntohl(). (in those name: h means 'host' or your computer, n means 'network', s means 'short' or 16bit value, l means 'long' or 32 bit value).
then you are on your own with serialization, C and C++ have no automatic way to perform it. some softwares can generate code to do it for you, like the ASN.1 implementation asn1c, but they are difficult to use because they involve much more than just copying data over the network.
Depending if you have enough place or not... you might simply use the streams :)
std::string Serialize() {
std::ostringstream out;
char version = '1';
out << version << senderId << '|' << sequenceNumber << '|' << data;
return out.str();
}
void Deserialize(const std::string& iString)
{
std::istringstream in(iString);
char version = 0, check1 = 0, check2 = 0;
in >> version;
switch(version)
{
case '1':
senderId >> check1 >> sequenceNumber >> check2 >> data;
break;
default:
// Handle
}
// You can check here than 'check1' and 'check2' both equal to '|'
}
I readily admit it takes more place... or that it might.
Actually, on a 32 bits architecture an int usually cover 4 bytes (4 char). Serializing them using streams only take more than 4 'char' if the value is superior to 9999, which usually gives some room.
Also note that you should probably include some guards in your stream, just to check when you get it back that it's alright.
Versioning is probably a good idea, it does not cost much and allows for unplanned later development.
You can have a class reprensenting the object you use in your software with all the niceties and member func and whatever you need. Then you have a 'serialized' struct that's more of a description of what will end up on the network.
To ensure the compiler will do whatever you tell him to do, you need to instruct it to 'pack' the structure. The directive I used here is for gcc, see your compiler doc if you're not using gcc.
Then the serialize and deserialize routine just convert between the two, ensuring byte order and details like that.
#include <arpa/inet.h> /* ntohl htonl */
#include <string.h> /* memcpy */
class Packet {
int senderId;
int sequenceNumber;
char data[MaxDataSize];
public:
char* Serialize();
void Deserialize(char *message);
};
struct SerializedPacket {
int senderId;
int sequenceNumber;
char data[MaxDataSize];
} __attribute__((packed));
void* Packet::Serialize() {
struct SerializedPacket *s = new SerializedPacket();
s->senderId = htonl(this->senderId);
s->sequenceNumber = htonl(this->sequenceNumber);
memcpy(s->data, this->data, MaxDataSize);
return s;
}
void Packet::Deserialize(void *message) {
struct SerializedPacket *s = (struct SerializedPacket*)message;
this->senderId = ntohl(s->senderId);
this->sequenceNumber = ntohl(s->sequenceNumber);
memcpy(this->data, s->data, MaxDataSize);
}
int senderId;
int sequenceNumber;
...
char *message = new char[MaxMailSize];
message[0] = senderId;
message[1] = sequenceNumber;
You're overwriting values here. senderId and sequenceNumber are both ints and will take up more than sizeof(char) bytes on most architectures. Try something more like this:
char * message = new char[MaxMailSize];
int offset = 0;
memcpy(message + offset, &senderId, sizeof(senderId));
offset += sizeof(senderId);
memcpy(message + offset, &sequenceNumber, sizeof(sequenceNumber));
offset += sizeof(sequenceNumber);
memcpy(message + offset, data, MaxDataSize);
EDIT:
fixed code written in a stupor. Also, as noted in comment, any such packet is not portable due to endian differences.
To answer your question generally, C++ has no reflection mechanism, and so manual serialize and unserialize functions defined on a per-class basis is the best you can do. That being said, the serialization function you wrote will mangle your data. Here is a correct implementation:
char * message = new char[MaxMailSize];
int net_senderId = htonl(senderId);
int net_sequenceNumber = htonl(sequenceNumber);
memcpy(message, &net_senderId, sizeof(net_senderId));
memcpy(message + sizeof(net_senderId), &net_sequenceNumber, sizeof(net_sequenceNumber));
As mentioned in other posts, senderId and sequenceNumber are both of type int, which is likely to be larger than char, so these values will be truncated.
If that's acceptable, then the code is OK. If not, then you need to split them into their constituent bytes. Given that the protocol you are using will specifiy the byte order of multi-byte fields, the most portable, and least ambiguous, way of doing this is through shifting.
For example, let's say that senderId and sequenceNumber are both 2 bytes long, and the protocol requires that the higher byte goes first:
char* Serialize() {
char *message = new char[MaxMailSize];
message[0] = senderId >> 8;
message[1] = senderId;
message[2] = sequenceNumber >> 8;
message[3] = sequenceNumber;
memcpy(&message[4], data, MaxDataSize);
return message;
}
I'd also recommend replacing the for loop with memcpy (if available), as it's unlikely to be less efficient, and it makes the code shorter.
Finally, this all assumes that char is one byte long. If it isn't, then all the data will need to be masked, e.g.:
message[0] = (senderId >> 8) & 0xFF;
You can use Protocol Buffers for defining and serializing of structs and classes. This is what google uses internally, and has a very small transfer mechanism.
http://code.google.com/apis/protocolbuffers/