I have an unsigned long long variable which I want to write to a binary file. However, I need to neglect all leading bytes which are zero.
This means
unsigned long long toWrite = 4;
should write 0x04 and not 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x04 to the stream.
#include <fstream>
int main(){
std::ofstream out("test.txt", std::ios::binary);
unsigned long long toWrite = 4;
out << cutoffZeroBytes(toWrite);
out.close();
return 1;
}
I was thinking about making cutoffZeroBytes a function which returns a char*. But if there are zero bytes in the middle (e.g. 0x03 0x00 0xf1), then I think I couldn't write it to the stream, since 0x00 determines the end of an char array.
I'm a little clueless here and need some help.
One way is using write for this aim.
So, just change:
out << cutoffZeroBytes(toWrite);
to:
out.write((char*)&toWrite, sizeof(toWrite));
And if you want to cut this number:
char* start = (char*)&toWrite;
int pi = sizeof(toWrite);
for (; pi > 0; pi--, start++)
if (*start)
break;
out.write(start, pi);
Related
I need to add a 64 bit floating point number into an unsigned char array at specific indexes (ex. index 1 through 8).
Example unsigned char array:
unsigned char msg[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
I want to add a floating point number like 0.084, for example, which is represented as 1B2FDD240681B53F in hex (little endian) to the unsigned char array at indexes 1,2,3,4,5,6,7,8 and leave indexes 0 and 9 unchanged.
So, I would like the unsigned char array, msg, to contain the following:
msg = {0x00, 0x1B, 0x2F, 0xDD, 0x24, 0x06, 0x81, 0xB5, 0x3F, 0x00}
So far I can get a std::string with the hexadecimal representation of the example floating point value 0.084 using the following code but I'm not sure how to add the string values back into the unsigned char array:
#include <iostream>
#include <sstream>
#include <iomanip>
using namespace std;
int main()
{
union udoub
{
double d;
unsigned long long u;
};
double dVal = 0.084;
udoub val;
val.d = dVal;
std::stringstream ss;
ss << std::setw(16) << std::setfill('0') << std::hex << val.u << std::endl;
std::string strValHexString = ss.str();
cout<< strValHexString << std::endl;
return 0;
}
Output:
3fb5810624dd2f1b
I tried using std::copy like in the example below to copy the values from the std::string to an unsigned char but it doesn't seem to do what I want:
unsigned char ucTmp[2];
std::copy(strValHexString.substr(0,2).begin(), strValHexString.substr(0,2).end(), ucTmp);
Looking for a C or C++ solution.
Formatting the component bytes into a hex string and then reading those back in again is a terrible waste of time and effort. Just use std::memcpy() (in C++) or memcpy (in C):
std::memcpy(&msg[1], &dVal, sizeof(dVal));
This will take care of any required pointer alignment issues. However, it will not do any 'interpretation' in terms of your endianness - but this shouldn't be a problem unless you're then transferring that byte array between different platforms.
Your example has undefined behaviour due to reading from an inactive member of a union. A well defined way to do the conversion to integer:
auto uVal = std::bit_cast<std::uint64_t>(dVal);
Now that you have the data in an integer, you can use bitwise operations to extract individual octets in specific positions:
msg[1] = (uVal >> 0x0 ) & 0xff;
msg[2] = (uVal >> 0x8 ) & 0xff;
msg[3] = (uVal >> 0x10) & 0xff;
msg[4] = (uVal >> 0x18) & 0xff;
msg[5] = (uVal >> 0x20) & 0xff;
...
This can be condensed into a loop.
Note that this works the same way regardless of endianness of the CPU. The resulting order in the array will always be little endian unlike in the direct std::memcpy approach which results in native endianness which is not necessarily little endian on all systems. However, if floating point and integers use different endianness, then the order won't be the same even with this approach.
In my project I'm working with QByteArrays appending data to them as the program goes. Most of the time, a simple quint8 gets appended just fine using QByteArray::append(). But when a quint16 gets appended, only 1 byte gets appended instead of 2.
QByteArray ba = QByteArray::fromHex("010203");
quint number(300);//300 in hex is 012c
ba.append(number);//What should be appended instead of just number?
//the current incorrect result is
ba.toHex() == "0102032c"
//the desired result is
ba.toHex() == "010203012c"
I've already tried this, but it just inserts the value as a string (4 bytes):
ba.append(QByteArray::number(number, 16));
What should I append to the QByteArray so both bytes of "number" get appended instead of just one byte? Also, the fastest method possible is preferred since this program needs to have great performance times. So absolutely no converting to QStrings.
Thanks for your time.
On its own, QByteArray only supports appending bytes; to append a big-endian representation of fixed-size integer types you can build your own operator<< (or what you prefer) overloads using the appropriate bit shifts:
QByteArray &operator<<(QByteArray &l, quint8 r)
{
l.append(r);
return l;
}
QByteArray &operator<<(QByteArray &l, quint16 r)
{
return l<<quint8(r>>8)<<quint8(r);
}
QByteArray &operator<<(QByteArray &l, quint32 r)
{
return l<<quint16(r>>16)<<quint16(r);
}
This allows you to write code like:
QByteArray b;
b<<quint16(300); // appends 0x01 0x2c
b<<quint8(4); // appends 0x04
b<<quint16(4); // appends 0x00 0x04
b<<quint32(123456); // appends 0x00 0x01 0xe2 0x40
b<<quint8(1)<<quin16(2)<<quint32(3); // appends 0x01 0x00 0x02 0x00 0x00 0x00 0x03
You should probably avoid writing
QByteArray b;
b<<1;
because in theory the output depends on the size of the current platform integer (although AFAIK on all platforms supported by Qt int is 32 bit).
I'm trying to create a simple binary format to transmit over a BlueToothLE module on Arduino. I'm trying to describe properties of a list of objects. And for starters I'm trying to transmit just a single property.
The format I'm attempting to encode and pass around is as follows.
namePropertyID, nameLength, nameString...
So given a name of "Bob"
0x01 0x03 0x42 0x6F 0x62
nameID 3 chars "B" "o" "b"
But when I pass the buffer around, it seems to mutate.
before I pass it, it reads:
0x01 0x03 0x42 0x6F 0x62
But after I pass it, it reads:
0x00 0x3C 0x18 0x04 0x00
Program.h
typedef enum {
InfoTypeName = 0x01
} InfoType;
class Program {
public:
char *name;
uint8_t * data();
uint8_t dataLen();
};
Program.cpp
#include "Program.h"
uint8_t* Program::data() {
uint8_t nameLength = strlen(name);
uint8_t buff[dataLen()];
buff[0] = InfoTypeName;
buff[1] = nameLength;
for (uint8_t i = 0; i < nameLength; i++) {
buff[i+2] = (uint8_t)name[i];
}
// First check of data, things look ok.
for (uint8_t i = 0; i < nameLength+2; i++) {
Serial.print(F(" 0x")); Serial.print(buff[i], HEX);
}
Serial.println();
return buff;
}
uint8_t Program::dataLen() {
return strlen(name) + 2;
}
Elsewhere I pass this to the Bluetooth library:
BTLEserial.write(program.data(), program.dataLen());
Which is implemented like so, and is printing out seemingly incorrect data:
size_t Adafruit_BLE_UART::write(uint8_t * buffer, uint8_t len)
{
Serial.print(F("\tWriting out to BTLE:"));
for (uint8_t i=0; i<len; i++) {
Serial.print(F(" 0x")); Serial.print(buffer[i], HEX);
}
Serial.println();
// actually sends the data over bluetooth here...
}
So a few questions:
Why the data mutating?
Is this a good approach for generating buffers?
Is having two separate methods, one for length and one for data a good pattern?
The problem is that in Program::data(), buff is a local variable. You are returning a pointer to its first element, which is a dangling pointer at the call side. You need to ensure the buffer you export is something that stays alive for long enough. There are different ways of doing this, but I am not entirely familiar with the limitations arduino places on what parts of the C and C++ standard libraries you can use.
The simplest approach could be to reserve a buffer in main, and pass that around to the code that populates it and consumes it. Alternatively, you could give your Program class a buffer data member. The main problem is going to be ensuring that the buffer is large enough for different messages.
I would first try something like this:
void create_msg_(const char* name, uint8_t buff, size_t size)
{
// populate buff with the message
}
void send_msg(const char* name)
{
size_t size = strlen(name) + 2;
uint8_t buff[size]; // VLA extension, not std C++
create_msg_(name, buff, size);
BTLEserial.write(buff, size);
}
I was recently in an interview and was given the question:
Look at this code and write its output:
unsigned char buff[] = { 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, 0x33 };
unsigned long *pD = (unsigned long *)buff;
unsigned short *pS = (unsigned short *)buff;
void *pEnd = &buff[sizeof(buff) - 1];
for (; pD < pEnd; pD++)
printf("0x%X\n", *pD);
for (; pS < pEnd; pS++)
printf("0x%X\n", *pS);
return 0;
Now assuming the system is 32bits and unsigned long is 4 bytes, the answer is:
0x11111111
0x22222222
0x33333333
0x1111
0x1111
0x2222
0x2222
0x3333
0x3333
Now for my question:
Why does it print only 16 bits with the unsigned short variable?
I know that unsigned short is 2 bytes but printf knows how much bytes to parse from the stack based on the %X, which is read as unsigned int. I would assume that it will read 4 bytes (unsigned int) at all times, with sometimes junk (or corrupted stack eventually).
What do you think?
Thanks!
*pS is of type unsigned short, so this value is used. Though printf expects an int for %X, you provide an unsigned short, which is promoted to int. So the order is: first 2 bytes are read from your array because *pS is unsigned short. Then this unsigned short value is promoted to int.
You don't have pointers in the output. By dereferencing you're telling printf to output a short and that's what it does. %X only displays that as hex.
I am trying to save a raw byte array into a file:
mDataStream.writeRawData( ( (const char *)&testPacket), 188);
The test packet is just an array of unsigned char, the packet is copied in the right size, but the bytes are reordered. ie: 0x47 0x00 0x10 0x20 ... becomes 0x00 0x47 ox20 0x10.
This looks like an endianness problem, but i've tried setting the byte order to little endian with unsuccessful results.