I'm trying to store binary data in a QR code. Apparently QR codes do support storing raw binary data (or ISO-8859-1 / Latin1). Here is what I want to encode (hex):
d1 50 01 00 00 00 f6 5f 05 2d 8f 0b 40 e2 01
I've tried the following encoders:
qr.js
Google Charts
qrcode.js
Decoding with zxing.org produces various incorrect results. The two javascript ones produce this (it's wrong; the first text character should be Ñ.
Whereas Google Charts produces this...
What is going on? Are any of these correct? What's really weird is that if I encode this sequence (with the JS ones at least) then it works fine - I would have thought the issue was non-ASCII characters but Ñ (0xd1) is non-ASCII.
d1 50 01 00 00 00 01 02 03 04 05 06 40 e2 01
Does anyone know what is going on?
Update
It occurred to me to try scanning them with a ZBar-based scanner app I found. It scans both JS versions ok (at least they start with ÑP). The Google Charts one is just wrong. So it seems like the issue is with ZXing (which is surprisingly shit - I wouldn't recommend it to anyone).
Update 2
ZBar can't handle null bytes. :-(
"What is going on? Are any of these correct?"
Except for the google chart (which is just empty), your QR codes are correct.
You can see the binary data from zxing is what you would expect:
4: Byte mode indicator
0f: length of 15 byte
d15001...: your 15 bytes of data
ec11 is just padding
The problem comes from the decoding. Because most decoders will try to interpret it as text. But since it's binary data, you should not try to handle it as text. Even if you think you can convert it from text to binary, as you saw this may cause issues with values which are not valid text.
So the solution is to use a decoder that will output you the binary data, and not text data.
Now about interpreting the QR code binary data as text, you said the first character should be 'Ñ' which is true if interpreted it as "ISO-8859-1",
which according to the QR code standard, is what should be done when there is no ECI mode defined.
But in practice, most smartphone QR code reader will interpret it as UTF-8 in this case (or at least try to auto-detect the encoding).
Even though this is not the standard, this had become common practice:
binary mode with no ECI, UTF-8 encoded text.
Maybe the reason behind it is that no one wants to waste these precious bytes adding an ECI mode specifying UTF-8. And actually, not all decoders support ECI.
There are two issues that you have to overcome to store binary data in QR codes.
ISO-8859-1 does not allow bytes in ranges of 00-1F and 7F-9F. If you
need to encode these bytes anyway, quote or encode them, i.e. use
quoted-printable or Base-64 encoding to avoid these ranges.
Since you are trying to store binary data in QR codes, you have to
rely only on your own scanner that will handle this binary data. You
don’t have to display text from your QR codes by other software,
like web application at zxing.org, because most QR decoders,
including that of zxing.org use heuristics to detect the character
set used. These heuristics may detect a character set other than
ISO-8859-1 and thus fail to properly display your binary data. Some
scanners use heuristics to detect a character set even if the
character set is explicitly given by ECI. This is why providing ECI
may not help much – scanners still use heuristics even with ECI.
So, using US-ASCII printable characters only (e.g., binary data encoded in Base64 before passing it to a QR Code generator) is the safest choice for QR code against the heuristics. This will also overcome another complication: that ISO-8859-1 was not the default encoding in earlier QR code standard published in 2000 (ISO/IEC 18004:2000). That standard did specify 8-bit Latin/Kana character set in accordance with JIS X 0201 (JIS8 also known as ISO-2022-JP) as default encoding for 8-bit mode, while the updated standard published in 2005 did change the default to ISO-8859-1.
As an alternative to Base-64, you can encode each byte with two hexadecimal characters (0-9, A-F), so, in the QR code your data will be encoded in the alphanumeric mode, not in 8-bit mode. This will disable all heuristics for sure and should not produce larger QR Code than with Base-64, because each character in the alphanumeric mode takes only 6 bits in the QR code stream.
Update:
I recently went back and published the referenced code as a project on GitHub for anyone who wants to use it.
https://github.com/yurelle/Base45Encoder
This is a bit necro, but I just hit this problem, and figured out a solution.
The problem with reading QR Codes with ZXING is that it assumes all QR Payloads are Strings. If you're willing to generate the QR Code in java with ZXING, I developed a solution which enables storing a binary payload in ZXING QR Codes with a storage efficiently loss of only -8%; better than the 33% inflation from Base64.
It exploits an internal compression optimization of the ZXING library based around pure Alphanum Strings. If you want a full explanation, with math and Unit Tests, check out my other answer.
But the short answer is this:
Solution
I implemented it as a self-contained static utility class, so all you have to do is call:
//Encode
final byte[] myBinaryData = ...;
final String encodedStr = BinaryToBase45Encoder.encodeToBase45QrPayload(myBinaryData);
//Decode
final byte[] decodedBytes = BinaryToBase45Encoder.decodeBase45QrPayload(encodedStr);
Alternatively, you can also do it via InputStreams:
//Encode
final InputStream in_1 = ... ;
final String encodedStr = BinaryToBase45Encoder.encodeToBase45QrPayload(in_1);
//Decode
final InputStream in_2 = ... ;
final byte[] decodedBytes = BinaryToBase45Encoder.decodeBase45QrPayload(in_2);
Here's the implementation
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
* For some reason none of the Java QR Code libraries support binary payloads. At least, none that
* I could find anyway. The commonly suggested workaround for this is to use Base64 encoding.
* However, this results in a 33% payload size inflation. If your payload is already near the size
* limit of QR codes, this is a lot.
*
* This class implements an encoder which takes advantage of a built-in compression optimization
* of the ZXING QR Code library, to enable the storage of Binary data into a QR Code, with a
* storage efficiency loss of only -8%.
*
* The built-in optimization is this: ZXING will automatically detect if your String payload is
* purely AlphaNumeric (by their own definition), and if so, it will automatically compress 2
* AlphaNumeric characters into 11 bits.
*
*
* ----------------------
*
*
* The included ALPHANUMERIC_TABLE is the conversion table used by the ZXING library as a reverse
* index for determining if a given input data should be classified as alphanumeric.
*
* See:
*
* com.google.zxing.qrcode.encoder.Encoder.chooseMode(String content, String encoding)
*
* which scans through the input string one character at a time and passes them to:
*
* getAlphanumericCode(int code)
*
* in the same class, which uses that character as a numeric index into the the
* ALPHANUMERIC_TABLE.
*
* If you examine the values, you'll notice that it ignores / disqualifies certain values, and
* effectively converts the input into base 45 (0 -> 44; -1 is interpreted by the calling code
* to mean a failure). This is confirmed in the function:
*
* appendAlphanumericBytes(CharSequence content, BitArray bits)
*
* where they pack 2 of these base 45 digits into 11 bits. This presents us with an opportunity.
* If we can take our data, and convert it into a compatible base 45 alphanumeric representation,
* then the QR Encoder will automatically pack that data into sub-byte chunks.
*
* 2 digits in base 45 is 2,025 possible values. 11 bits has a maximum storage capacity of 2,048
* possible states. This is only a loss of 1.1% in storage efficiency behind raw binary.
*
* 45 ^ 2 = 2,025
* 2 ^ 11 = 2,048
* 2,048 - 2,025 = 23
* 23 / 2,048 = 0.01123046875 = 1.123%
*
* However, this is the ideal / theoretical efficiency. This implementation processes data in
* chunks, using a Long as a computational buffer. However, since Java Long's are singed, we
* can only use the lower 7 bytes. The conversion code requires continuously positive values;
* using the highest 8th byte would contaminate the sign bit and randomly produce negative
* values.
*
*
* Real-World Test:
*
* Using a 7 byte Long to encode a 2KB buffer of random bytes, we get the following results.
*
* Raw Binary Size: 2,048
* Encoded String Size: 3,218
* QR Code Alphanum Size: 2,213 (after the QR Code compresses 2 base45 digits to 11 bits)
*
* This is a real-world storage efficiency loss of only 8%.
*
* 2,213 - 2,048 = 165
* 165 / 2,048 = 0.08056640625 = 8.0566%
*/
public class BinaryToBase45Encoder {
public final static int[] ALPHANUMERIC_TABLE;
/*
* You could probably just copy & paste the array literal from the ZXING source code; it's only
* an array definition. But I was unsure of the licensing issues with posting it on the internet,
* so I did it this way.
*/
static {
final Field SOURCE_ALPHANUMERIC_TABLE;
int[] tmp;
//Copy lookup table from ZXING Encoder class
try {
SOURCE_ALPHANUMERIC_TABLE = com.google.zxing.qrcode.encoder.Encoder.class.getDeclaredField("ALPHANUMERIC_TABLE");
SOURCE_ALPHANUMERIC_TABLE.setAccessible(true);
tmp = (int[]) SOURCE_ALPHANUMERIC_TABLE.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();//Shouldn't happen
tmp = null;
} catch (IllegalAccessException e) {
e.printStackTrace();//Shouldn't happen
tmp = null;
}
//Store
ALPHANUMERIC_TABLE = tmp;
}
public static final int NUM_DISTINCT_ALPHANUM_VALUES = 45;
public static final char[] alphaNumReverseIndex = new char[NUM_DISTINCT_ALPHANUM_VALUES];
static {
//Build AlphaNum Index
final int len = ALPHANUMERIC_TABLE.length;
for (int x = 0; x < len; x++) {
// The base45 result which the alphanum lookup table produces.
// i.e. the base45 digit value which String characters are
// converted into.
//
// We use this value to build a reverse lookup table to find
// the String character we have to send to the encoder, to
// make it produce the given base45 digit value.
final int base45DigitValue = ALPHANUMERIC_TABLE[x];
//Ignore the -1 records
if (base45DigitValue > -1) {
//The index into the lookup table which produces the given base45 digit value.
//
//i.e. to produce a base45 digit with the numeric value in base45DigitValue, we need
//to send the Encoder a String character with the numeric value in x.
alphaNumReverseIndex[base45DigitValue] = (char) x;
}
}
}
/*
* The storage capacity of one digit in the number system; i.e. the maximum
* possible number of distinct values which can be stored in 1 logical digit
*/
public static final int QR_PAYLOAD_NUMERIC_BASE = NUM_DISTINCT_ALPHANUM_VALUES;
/*
* We can't use all 8 bytes, because the Long is signed, and the conversion math
* requires consistently positive values. If we populated all 8 bytes, then the
* last byte has the potential to contaminate the sign bit, and break the
* conversion math. So, we only use the lower 7 bytes, and avoid this problem.
*/
public static final int LONG_USABLE_BYTES = Long.BYTES - 1;
//The following mapping was determined by brute-forcing -1 Long (all bits 1), and compressing to base45 until it hit zero.
public static final int[] BINARY_TO_BASE45_DIGIT_COUNT_CONVERSION = new int[] {0,2,3,5,6,8,9,11,12};
public static final int NUM_BASE45_DIGITS_PER_LONG = BINARY_TO_BASE45_DIGIT_COUNT_CONVERSION[LONG_USABLE_BYTES];
public static final Map<Integer, Integer> BASE45_TO_BINARY_DIGIT_COUNT_CONVERSION = new HashMap<>();
static {
//Build Reverse Lookup
int len = BINARY_TO_BASE45_DIGIT_COUNT_CONVERSION.length;
for (int x=0; x<len; x++) {
int numB45Digits = BINARY_TO_BASE45_DIGIT_COUNT_CONVERSION[x];
BASE45_TO_BINARY_DIGIT_COUNT_CONVERSION.put(numB45Digits, x);
}
}
public static String encodeToBase45QrPayload(final byte[] inputData) throws IOException {
return encodeToBase45QrPayload(new ByteArrayInputStream(inputData));
}
public static String encodeToBase45QrPayload(final InputStream in) throws IOException {
//Init conversion state vars
final StringBuilder strOut = new StringBuilder();
int data;
long buf = 0;
// Process all input data in chunks of size LONG.BYTES, this allows for economies of scale
// so we can process more digits of arbitrary size before we hit the wall of the binary
// chunk size in a power of 2, and have to transmit a sub-optimal chunk of the "crumbs"
// left over; i.e. the slack space between where the multiples of QR_PAYLOAD_NUMERIC_BASE
// and the powers of 2 don't quite line up.
while(in.available() > 0) {
//Fill buffer
int numBytesStored = 0;
while (numBytesStored < LONG_USABLE_BYTES && in.available() > 0) {
//Read next byte
data = in.read();
//Push byte into buffer
buf = (buf << 8) | data; //8 bits per byte
//Increment
numBytesStored++;
}
//Write out in lower base
final StringBuilder outputChunkBuffer = new StringBuilder();
final int numBase45Digits = BINARY_TO_BASE45_DIGIT_COUNT_CONVERSION[numBytesStored];
int numB45DigitsProcessed = 0;
while(numB45DigitsProcessed < numBase45Digits) {
//Chunk out a digit
final byte digit = (byte) (buf % QR_PAYLOAD_NUMERIC_BASE);
//Drop digit data from buffer
buf = buf / QR_PAYLOAD_NUMERIC_BASE;
//Write Digit
outputChunkBuffer.append(alphaNumReverseIndex[(int) digit]);
//Track output digits
numB45DigitsProcessed++;
}
/*
* The way this code works, the processing output results in a First-In-Last-Out digit
* reversal. So, we need to buffer the chunk output, and feed it to the OutputStream
* backwards to correct this.
*
* We could probably get away with writing the bytes out in inverted order, and then
* flipping them back on the decode side, but just to be safe, I'm always keeping
* them in the proper order.
*/
strOut.append(outputChunkBuffer.reverse().toString());
}
//Return
return strOut.toString();
}
public static byte[] decodeBase45QrPayload(final String inputStr) throws IOException {
//Prep for InputStream
final byte[] buf = inputStr.getBytes();//Use the default encoding (the same encoding that the 'char' primitive uses)
return decodeBase45QrPayload(new ByteArrayInputStream(buf));
}
public static byte[] decodeBase45QrPayload(final InputStream in) throws IOException {
//Init conversion state vars
final ByteArrayOutputStream out = new ByteArrayOutputStream();
int data;
long buf = 0;
int x=0;
// Process all input data in chunks of size LONG.BYTES, this allows for economies of scale
// so we can process more digits of arbitrary size before we hit the wall of the binary
// chunk size in a power of 2, and have to transmit a sub-optimal chunk of the "crumbs"
// left over; i.e. the slack space between where the multiples of QR_PAYLOAD_NUMERIC_BASE
// and the powers of 2 don't quite line up.
while(in.available() > 0) {
//Convert & Fill Buffer
int numB45Digits = 0;
while (numB45Digits < NUM_BASE45_DIGITS_PER_LONG && in.available() > 0) {
//Read in next char
char c = (char) in.read();
//Translate back through lookup table
int digit = ALPHANUMERIC_TABLE[(int) c];
//Shift buffer up one digit to make room
buf *= QR_PAYLOAD_NUMERIC_BASE;
//Append next digit
buf += digit;
//Increment
numB45Digits++;
}
//Write out in higher base
final LinkedList<Byte> outputChunkBuffer = new LinkedList<>();
final int numBytes = BASE45_TO_BINARY_DIGIT_COUNT_CONVERSION.get(numB45Digits);
int numBytesProcessed = 0;
while(numBytesProcessed < numBytes) {
//Chunk out 1 byte
final byte chunk = (byte) buf;
//Shift buffer to next byte
buf = buf >> 8; //8 bits per byte
//Write byte to output
//
//Again, we need to invert the order of the bytes, so as we chunk them off, push
//them onto a FILO stack; inverting their order.
outputChunkBuffer.push(chunk);
//Increment
numBytesProcessed++;
}
//Write chunk buffer to output stream (in reverse order)
while (outputChunkBuffer.size() > 0) {
out.write(outputChunkBuffer.pop());
}
}
//Return
out.flush();
out.close();
return out.toByteArray();
}
}
Just at a glance, the qr formats are different. I'd compare the qr formats to see if it's a problem of error correction or encoding or something else.
It turned out that ZXing is just crap, and ZBar does some weird stuff with the data (converting it to UTF-8 for example). I managed to get it to output the raw data including null bytes though. Here is a patch for the best Android ZBar library I found, that has now been merged.
I used System.Convert.ToBase64String to convert the supplied sample byte array into a Base64-encoded string, then I used ZXing to create a QRCode image.
Next I called ZXing to read the string back from the generated QRCode, and then called System.Convert.FromBase64String to convert the string back into a byte array.
I confirm that the data completed the round trip successfully.
The informational RFC 9285 - The Base45 Data Encoding document describing the optimal scheme for storing binary data within the constraints of QR Alphanumeric Mode was recently published by the IETF.
(one positive side-effect of ongoing standardization work surrounding Health Certificate QR-codes)
Related
I'm trying to fuzz a cookie with Zaproxy. The cookie is missing one character so i made a prefix processor with the cookie md5 hash and am adding an alphanumeric character to the end..all good but the original value is encoded twice..once with base64 then with hex. I'm trying to find a hex payload processor to accomplish this but it doesn't seem to exist although there is an option under the menu item 'encode/decode/hash' to encode or decode from/to ASCII hex. Is there another way to do this?
I believe this does what you need: https://github.com/zaproxy/community-scripts/blob/main/payloadprocessor/to-hex.js
/**
* Converts a string payload to hex.
*
* Created to add functionality found in Burp to solve Natas19
* https://www.youtube.com/watch?v=z3RtpWZ_R3Q
*
* EN10
*/
function process(payload) {
var hex = '';
var i;
for (i = 0; i < payload.length; i++) {
hex += payload.charCodeAt(i).toString(16);
}
return hex;
}
According to this comment: Javascript character (ASCII) to Hex you might have to add a bit of handling for any characters whose code point is below 16.
I an working on ECIES and need to load peer public key.
Load EC Public key
I an using ECDH and need to load peer public key.
When I try to load public key from PEM file , seems no issue
Issue here:
EVP_PKEY * get_peer_key()
{
// base64 certificate data of alice_pub_key.pem
char *buffer= "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjWrT7F97QrSqGrlIgPK8dphNBicNO6gDLfOIMjhF2MiLuuzd7L7BP+bLCuNtKKe/2dOkgPqgXv4BFWqgp6PZXQ=="`
// calculate buffer length
int l = strlen(buffer)
//create bio from buffer
BIO *in = BIO_new_mem_buf(buffer,l)
//gnerate ec key
EC_KEY *eckey = PEM_read_bio_EC_PUBKEY(in,NULL,NULL,NULL)` // ==> FAIL
//need to convert to EVP format
EVP_PKEY *peerKey = EVP_PKEY_new()
//assign ec key evp
if(EVP_PKEY_assign_EC_KEY(peerKey,eckey) != 1 )
printf("\n error hapened");
return peerKey;
}
Works fine:
EVP_PKEY * get_peer_key()
{
//Load PEM format file
char * infile = "alice_pub_key.pem";
//create bio
BIO *in = BIO_new(BIO_s_file());
//read bio file
BIO_read_filename(in , infile);
//create eckey
EC_KEY *eckey = PEM_read_bio_EC_PUBKEY(in,NULL,NULL,NULL); // ==> success
// create peer key
EVP_PKEY *peerKey = EVP_PKEY_new();
//assign public key
if(EVP_PKEY_assign_EC_KEY(peerKey,eckey) != 1 )
printf("\n error hapened");
return peerKey;
}
Can some one suggest whats going wrong while reading base64 data of pem file
There are two ways of solving this:
Creating a PEM using a header and footer line and line breaks (at the 64th character;
Base 64 decoding the text and then handling it by parsing the resulting ASN.1 / DER binary;
I'd prefer the latter, as I abhor adding lines and such, it is error prone at best, and string manipulations should be avoided where possible.
Note that this assumes that the base 64 contains a SubjectPublicKeyInfo structure which I've shown you earlier. Otherwise you may have to find out how to parse a X9.62 structure or just a point.
So here is the thing, I'm receiving 1 byte from Bluetooth transmission. When using QDebug I get this message:
The array with error has "\x06"
The line that fails is this:
bool ok = true;
int v = value.toInt(&ok,0);
Because ok has false. But I'm trying to wrap my head around the fact that, How can the conversion fail in the first place if the data represented in that byte (as a sequence of zeros and ones) will always have a valid integer representation. (one byte can always be represented as a int between -127 and 128). So I'm left with the question, how can the conversion fail?
Reading the documentation does not provide many clues as it does not say how the byte array will be interpreted.
QByteArray::toInt converts a string representation in the default C locale to an integer. That means to successfully convert the value in your example, your byte array must contain the string "0x06", which consists of 4 bytes.
To convert a single byte to an int, just extract it:
int i = value[0];
Type promotion will widen the char to an int
QtWebEngine uses a IPC mechanism to communicate between the C+ Qt world and the JavaScript work. This mechanism is used for QWebChannel, and it appears to be based on WebSockets. Is there a way to use the underlying IPC or WebSockets without using QWebChannel, as the latter seems restricted to strings or JSON-encoded data?
Background: I wrote an application QtDomTerm which is a JavaScript-based terminal emulator that uses QWebChannel to connect input/output from a PTY to QtWebEngine. This works fairly well, but there is a glitch relating to utf8/string conversion. Ideally, I'd like to send raw bytes from the PTY, and do byte-to-text conversion in JavaScript. But QWebChannel is too high-level and only handles strings or JSON-encoded data. It does not handle QByteArray.
Of course there are multiple ways to solve my problem. One is to manually create a WebSocket server, and have the JavaScript running in the QtWebEngine connect to it. But it appears that is what is going on behind the scene anyway, using qt.webChannelTransport. It seems like it would be most efficient and elegant if I could access the underlying transport (the class WebChannelIPCTransportHost seems to be relevant).
Anyone tried something like this? I.e. I would like to not use QWebChannel - unless there is an efficient way for it to pass a QByteArray.
(I rephrased the question. There was a comment about missing research, but I've browsed heavily though the Qt docuemntation, source code, and here, without finding a clear answer.)
What prevents you from sending QString::fromLatin1(data.toHex()), where data is of the QByteArray type? That's all you need, really. Use a reverse conversion on the javascript side, see e.g. this question.
Since there doesn't seem to be a way to go "below" the QWebChannel (without hacking Qt itself) there seemed to be three options:
Create a separate WebSockets connection. That would require bigger non-local changes, and possibly creating extra processes.
Do the byte-to-text decoding on the C++ side, maybe using QTextDecoder. That would probably be simplest. However, there are architectural reasons for preferring to do text decoding close to where we do escape sequence processing. That is certainly more traditional, and may better handle corner cases. Plus DomTerm needs to work with other front-ends than the QtDomTerm front-end.
Encode the bytestream to a sequence of strings, transmit the latter using QWebChannel, convert back to bytes on the JavaScript side, and then handle text-decoding in JavaScript.
I chose to implement the third option. There is some overhead in therms of converting back-end-forth. However, I designed and implemented an encoding that is quite efficient:
/** Encode an arbitrary sequence of bytes as an ASCII string.
* This is used because QWebChannel doesn't have a way to transmit
* data except as strings or JSON-encoded strings.
* We restrict the encoding to ASCII (i.e. codes less then 128)
* to avoid excess bytes if the result is UTF-8-encoded.
*
* The encoding optimizes UTF-8 data, with the following byte values:
* 0-3: 1st byte of a 2-byte sequence encoding an arbitrary 8-bit byte.
* 4-7: 1st byte of a 2-byte sequence encoding a 2-byte UTF8 Latin-1 character.
* 8-13: mean the same ASCII control character
* 14: special case for ESC
* 15: followed by 2 more bytes encodes a 2-byte UTF8 sequence.
* bytes 16-31: 1st byte of a 3-byte sequence encoding a 3-byte UTF8 sequence.
* 32-127: mean the same ASCII printable character
* The only times we generate extra bytes for a valid UTF8 sequence
* if for code-points 0-7, 14-26, 28-31, 0x100-0x7ff.
* A byte that is not part of a valid UTF9 sequence may need 2 bytes.
* (A character whose encoding is partial, may also need extra bytes.)
*/
static QString encodeAsAscii(const char * buf, int len)
{
QString str;
const unsigned char *ptr = (const unsigned char *) buf;
const unsigned char *end = ptr + len;
while (ptr < end) {
unsigned char ch = *ptr++;
if (ch >= 32 || (ch >= 8 && ch <= 13)) {
// Characters in the printable ascii range plus "standard C"
// control characters are encoded as-is
str.append(QChar(ch));
} else if (ch == 27) {
// Special case for ESC, encoded as '\016'
str.append(QChar(14));
} else if ((ch & 0xD0) == 0xC0 && end - ptr >= 1
&& (ptr[0] & 0xC0) == 0x80) {
// Optimization of 2-byte UTF-8 sequence
if ((ch & 0x1C) == 0) {
// If Latin-1 encode 110000aa,10bbbbbb as 1aa,0BBBBBBB
// where BBBBBBB=48+bbbbbb
str.append(4 + QChar(ch & 3));
} else {
// Else encode 110aaaaa,10bbbbbb as '\017',00AAAAA,0BBBBBBB
// where AAAAAA=48+aaaaa;BBBBBBB=48+bbbbbb
str.append(QChar(15));
str.append(QChar(48 + (ch & 0x3F)));
}
str.append(QChar(48 + (*ptr++ & 0x3F)));
} else if ((ch & 0xF0) == 0xE0 && end - ptr >= 2
&& (ptr[0] & 0xC0) == 0x80 && (ptr[1] & 0xC0) == 0x80) {
// Optimization of 3-byte UTF-8 sequence
// encode 1110aaaa,10bbbbbb,10cccccc as AAAA,0BBBBBBB,0CCCCCCC
// where AAAA=16+aaaa;BBBBBBB=48+bbbbbb;CCCCCCC=48+cccccc
str.append(QChar(16 + (ch & 0xF)));
str.append(QChar(48 + (*ptr++ & 0x3F)));
str.append(QChar(48 + (*ptr++ & 0x3F)));
} else {
// The fall-back case - use 2 bytes for 1:
// encode aabbbbbb as 000000aa,0BBBBBBB, where BBBBBBB=48+bbbbbb
str.append(QChar((ch >> 6) & 3));
str.append(QChar(48 + (ch & 0x3F)));
}
}
return str;
}
Yes, this is overkill and almost certainly premature optimization, but I can't help myself :-)
I need to send floating point numbers using a UDP connection to a Qt application. Now in Qt the only function available is
qint64 readDatagram ( char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0 )
which accepts data in the form of signed character buffer. I can convert my float into a string and send it but it will obviously not be very efficient converting a 4 byte float into a much longer sized character buffer.
I got hold of these 2 functions to convert a 4 byte float into an unsinged 32 bit integer to transfer over network which works fine for a simple C++ UDP program but for Qt I need to receive the data as unsigned char.
Is it possible to avoid converting the floatinf point data into a string and then sending it?
uint32_t htonf(float f)
{
uint32_t p;
uint32_t sign;
if (f < 0) { sign = 1; f = -f; }
else { sign = 0; }
p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // Whole part and sign.
p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // Fraction.
return p;
}
float ntohf(uint32_t p)
{
float f = ((p>>16)&0x7fff); // Whole part.
f += (p&0xffff) / 65536.0f; // Fraction.
if (((p>>31)&0x1) == 0x1) { f = -f; } // Sign bit set.
return f;
}
Have you tried using readDatagram? Or converting the data to a QByteArray after reading? In many cases a char* is really just a byte array. This is one of those cases. Note that the writeDatagram can take a QByteArray.
Generally every thing sent across sockets is in bytes not strings, layers on either end do the conversions. Take a look here, especially the Broadcaster examples. They show how to create a QByteArray for broadcast and receive.
Not sure why the downvote, since the question is vague in requirements.
A 4-byte float is simply a 4 character buffer, if cast as one. If the systems are homogenous, the float can be sent as a signed char *, and bit for bit it'll be the same read into the signed char * on the receiver directly, no conversion needed. If the systems are heterogenous, then this won't work and you need to convert it to a portable format, anyway. IEEE format is often used, but my question is still, what are the requirements, is the float format the same between systems?
If I read it correctly, your primary question seems to be how to receive data of type unsigned char with QT's readDatagram function which uses a pointer to a buffer of type char.
The short answer is use a cast along these lines:
const size_t MAXSIZE = 1024;
unsigned char* data = malloc(MAXSIZE);
readDatagram ( (unsigned char *)data, MAXSIZE, address, port )
I'm going to assume you have multiple machines which use the same IEEE floating point format but some of which are big endian and some of which are little endian. See this SO post for a good discussion of this issue.
In that case you could do something a bit simpler like this:
const size_t FCOUNT = 256;
float* data = malloc(FCOUNT * sizeof(*data));
readDatagram ( (char *)data, FCOUNT * sizeof(*data), address, port )
for (int i = 0; i != FCOUNT; ++i)
data[i] = ntohf(*((uint32_t*)&data[i]));
The thing to remember is that as far as networking functions like readDatagram are concerned, the data is just a bunch of bits and it doesn't care what type those bits are interpreted as.
If both ends of your UDP connection use Qt, I would suggest looking at QDataStream. You can create this from a QByteArray each time you read a datagram, and then read whatever values you require - floats, maps, lists, QVariants, and of course string.
Similarly, on the sending side, you'd create a data stream, push data into it, then send the resulting QByteArray over writeDatagram.
Obviously this only works if both ends use Qt - the data encoding is well-defined, but non-trivial to generate by hand.
(If you want stream orientated behaviour, you could use the fact that QUDPSocket is a QIODevice with a data-stream, but it sounds as if you want per-datagram behaviour)