Search code examples
qtqbytearray

Unexpected behaviour in copying QByteArray


I am working on decoding a HDLC-like protocol and started from some code found on web (https://github.com/jarkko-hautakorpi/Arduhdlc). The following code is an excerpt to focus on where I am having problems. It reads a QByteArray (coming from a serial) and then it drops initial and final frame boundary bytes, it manages escaping sequence and checks the CRC

#include <QCoreApplication>
#include <QDebug>
#include "hdlc_qt.h"

#define FRAME_BOUNDARY_OCTET 0x7E
#define CONTROL_ESCAPE_OCTET 0x7D
#define INVERT_OCTET 0x20
QByteArray receive_frame_buffer;
quint16 frame_position;
quint16 frame_checksum;
bool escape_character;


bool hdlcFrameCRC_check(int frame_index);

void charReceiver(QByteArray dataArray)
{

    quint8 data = 0;
    qDebug() << "Received:\t"<<dataArray.toHex();
    for(QByteArray::iterator it = dataArray.begin(); it != dataArray.end(); it++) {
        data = (*it);
        //qDebug ("data: %x",data);
        /* Start flag or end flag */
        if(data == FRAME_BOUNDARY_OCTET) {

            qDebug("FRAME_BOUNDARY_OCTET: %x",data);
            qDebug() << "frame_position:"<<frame_position;
            if(escape_character == true) {
                escape_character = false;
            }
            /* Do CRC check if frame is valid */

            else if(  (frame_position >= 2) && (hdlcFrameCRC_check(frame_position)) )  {
                /* Call user defined function to handle HDLC frame */
                qDebug() << "ValidFrame"<<receive_frame_buffer.toHex();

            }
            qDebug() << "frame_buffer:\t"<<receive_frame_buffer.toHex();
            /* Reset all for next frame */
            frame_position = 0;
            frame_checksum = 0;
            receive_frame_buffer.clear();
            continue;

        }

        if(escape_character) {
            escape_character = false;
            data ^= INVERT_OCTET;
        } else if(data == CONTROL_ESCAPE_OCTET) {
            escape_character = true;
            continue;
        }
        //receive_frame_buffer[frame_position] = data;
        receive_frame_buffer.append(data); //changed to avoid compiler warning

        // qDebug ("rx_framebuf[%d] = %x",frame_position,receive_frame_buffer.at(frame_position));

        frame_position++;

        /* If we don't ever receive valid frame,
         * buffer will keep growing bigger and bigger.
         * Hard coded max size limit and then reset
         */
        if(frame_position >= 2048)
        {
            receive_frame_buffer.clear();
            frame_position = 0;
            frame_checksum = 0;
        }
    }
}

bool hdlcFrameCRC_check(int frame_index)
{
    /* frame = ...[CRC-LO] [CRC-HI] */
    quint16 crc_received = 0;
    crc_received = receive_frame_buffer[frame_index-1]; // msb
    crc_received = crc_received << 8;
    crc_received |= receive_frame_buffer[frame_index-2]; // lsb
    quint16 crc_calculated = qChecksum((const char*)receive_frame_buffer.constData(), frame_index-2);
    //crc_calculated = crc_calculated^0xFFFF;

    qDebug("CRC received:%x",crc_received);
    qDebug("CRC calculated:%x",crc_calculated);

    if(crc_received == crc_calculated) {
        return true;
    } else {
        return false;
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);


    QByteArray test1 = QByteArray("\x7E\x08\x1A\x00\x00\x07\x2B\x08\x00\x08\x16\x00\x00\x08\x8E\x07\xF7\x22\x20\x7E",20);
    QByteArray test2 = QByteArray("\x7e\x08\x22\x00\x00\x07\x28\x08\x08\x08\x10\x00\x05\x08\x95\x07\xff\xf6\x41\x7e",20);
    QByteArray test3 = QByteArray("\x7e\x08\x22\x00\x00\x07\x21\x08\x09\x08\x10\x00\x07\x08\x8f\x08\x00\xe7\x89\x7e",20);
    QByteArray test4 = QByteArray("\x7e\x08\x0e\x00\x00\x07\x2f\x08\x07\x08\x1b\x00\x00\x08\x93\x07\xf8\x9a\xb1\x7e",20);
    QByteArray test5 = QByteArray("\x7e\x08\x27\x00\x00\x07\x27\x08\x0a\x08\x10\x00\x05\x08\x93\x07\xfd\x18\x53\x7e",20);

    QVector<QByteArray> test;
    test.push_back(test1);
    test.push_back(test2);
    test.push_back(test3);
    test.push_back(test4);
    test.push_back(test5);

    for(int i = 0; i < test.size();++i) {
        qDebug()<<"-------------------------------------------------------";
        qDebug()<<"Test #" << i+1<< ":\t "<< test[i].toHex();
        charReceiver(test[i]);

    }
    return a.exec();
}

I have prepared some tests, named test1,...,test5 and spotted that it fails in the CRC verification step. The problem is that on some tests the CRC sequence passed to the method is something that is not present in the initial sequence. For test1 che CRC sequence ("\x22\x20") is passed correctly, while in test2 instead of "\xf6\x41" the method receives "\xff\xf6" and similarly for test3 and test4, while test5 is correct. Do you have any possible explanation and remedy?


Solution

  • Always remember to check for intermediate values.

    I don't really know the underlying aspects, but it seems that QByteArray[index] may result in a 0xffxx for quint16 whenever the result is greater than 127 (8 bits).

    Just do a QDebug for receive_frame_buffer[...]: for instance, with the fourth test, which should result in b1 (index - 1) and 9a (index - 2), you actually get:

    CRC1: ffb1
    CRC2: ff9a
    

    Which, obviously, results in ff9a after shifting and using the | operator.

    Unfortunately, I have no experience with C++, so there may be a more elegant way to do so, but if you want to avoid further temporary variables, the simplest solution is to mask the QByteArray[] result with 0xff:

    crc_received = receive_frame_buffer[frame_index-1] & 0xff;
    crc_received = crc_received << 8;
    crc_received |= receive_frame_buffer[frame_index-2] & 0xff;