1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-08-06 19:06:30 -04:00
Files
sdrangel/sdrbase/util/simpleserializer.cpp
T
Robin Getz 742746dc7b Fix undefined shift in SimpleDeserializer::readS32
The signed integer deserialization code used a signed temporary value while
assembling bytes:

    tmp = (tmp << 8) | byte;

For negative values, tmp was initialized to -1 for sign extension, causing a
left shift of a negative value, which is undefined behavior in C++.

Use an unsigned temporary while constructing the 32-bit representation and
convert to qint32 only after all bytes have been assembled. This preserves the
existing two's complement sign handling while avoiding undefined signed shifts.

Fixes static analysis (cppcheck) warning about shifting a negative value.

Signed-off-by: Robin Getz <rgetz503@gmail.com>
2026-08-01 15:01:21 -04:00

745 lines
17 KiB
C++

///////////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2014 John Greb <hexameron@spam.no> //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////////
#include <stdint.h>
#include "util/simpleserializer.h"
#if __WORDSIZE == 64
#define PRINTF_FORMAT_S32 "%d"
#define PRINTF_FORMAT_U32 "%u"
#define PRINTF_FORMAT_S64 "%d"
#define PRINTF_FORMAT_U64 "%u"
#else
#define PRINTF_FORMAT_S32 "%d"
#define PRINTF_FORMAT_U32 "%u"
#define PRINTF_FORMAT_S64 "%lld"
#define PRINTF_FORMAT_U64 "%llu"
#endif
template <bool> struct Assert { };
typedef Assert<(bool(sizeof(float) == 4))> float_must_be_32_bits[bool(sizeof(float) == 4) ? 1 : -1];
typedef Assert<(bool(sizeof(double) == 8))> double_must_be_64_bits[bool(sizeof(double) == 8) ? 1 : -1];
SimpleSerializer::SimpleSerializer(uint version) :
m_data(),
m_finalized(false)
{
m_data.reserve(100);
// write version information
int length;
if(version >= (1 << 24))
length = 4;
else if(version >= (1 << 16))
length = 3;
else if(version >= (1 << 8))
length = 2;
else if(version > 0)
length = 1;
else length = 0;
if(!writeTag(TVersion, 0, length))
return;
length--;
for(int i = length; i >= 0; i--)
m_data.push_back((char)((version >> (i * 8)) & 0xff));
}
void SimpleSerializer::writeS32(quint32 id, qint32 value)
{
int length;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if((value < -(1 << 23)) || (value >= (1 << 23)))
length = 4;
else if((value < -(1 << 15)) || (value >= (1 << 15)))
length = 3;
else if((value < -(1 << 7)) || (value >= (1 << 7)))
length = 2;
else if(value != 0)
length = 1;
else length = 0;
if(!writeTag(TSigned32, id, length))
return;
length--;
for(int i = length; i >= 0; i--)
m_data.push_back((char)((value >> (i * 8)) & 0xff));
}
void SimpleSerializer::writeU32(quint32 id, quint32 value)
{
int length;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(value >= (1 << 24))
length = 4;
else if(value >= (1 << 16))
length = 3;
else if(value >= (1 << 8))
length = 2;
else if(value > 0)
length = 1;
else length = 0;
if(!writeTag(TUnsigned32, id, length))
return;
length--;
for(int i = length; i >= 0; i--)
m_data.push_back((char)((value >> (i * 8)) & 0xff));
}
void SimpleSerializer::writeS64(quint32 id, qint64 value)
{
int length;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if((value < -(1ll << 55)) || (value >= (1ll << 55)))
length = 8;
else if((value < -(1ll << 47)) || (value >= (1ll << 47)))
length = 7;
else if((value < -(1ll << 39)) || (value >= (1ll << 39)))
length = 6;
else if((value < -(1ll << 31)) || (value >= (1ll << 31)))
length = 5;
else if((value < -(1ll << 23)) || (value >= (1ll << 23)))
length = 4;
else if((value < -(1ll << 15)) || (value >= (1ll << 15)))
length = 3;
else if((value < -(1ll << 7)) || (value >= (1ll << 7)))
length = 2;
else if(value != 0)
length = 1;
else length = 0;
if(!writeTag(TSigned64, id, length))
return;
length--;
for(int i = length; i >= 0; i--)
m_data.push_back((char)((value >> (i * 8)) & 0xff));
}
void SimpleSerializer::writeU64(quint32 id, quint64 value)
{
int length;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(value >= (1ll << 56))
length = 8;
else if(value >= (1ll << 48))
length = 7;
else if(value >= (1ll << 40))
length = 6;
else if(value >= (1ll << 32))
length = 5;
else if(value >= (1ll << 24))
length = 4;
else if(value >= (1ll << 16))
length = 3;
else if(value >= (1ll << 8))
length = 2;
else if(value > 0)
length = 1;
else length = 0;
if(!writeTag(TUnsigned64, id, length))
return;
length--;
for(int i = length; i >= 0; i--)
m_data.push_back((char)((value >> (i * 8)) & 0xff));
}
union floatasint {
quint32 u;
float f;
};
void SimpleSerializer::writeFloat(quint32 id, float value)
{
union floatasint tmp;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(!writeTag(TFloat, id, 4))
return;
tmp.f = value;
m_data.push_back((char)((tmp.u >> 24) & 0xff));
m_data.push_back((char)((tmp.u >> 16) & 0xff));
m_data.push_back((char)((tmp.u >> 8) & 0xff));
m_data.push_back((char)(tmp.u & 0xff));
}
union doubleasint {
quint64 u;
double d;
};
void SimpleSerializer::writeDouble(quint32 id, double value)
{
union doubleasint tmp;
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(!writeTag(TDouble, id, 8))
return;
tmp.d = value;
m_data.push_back((char)((tmp.u >> 56) & 0xff));
m_data.push_back((char)((tmp.u >> 48) & 0xff));
m_data.push_back((char)((tmp.u >> 40) & 0xff));
m_data.push_back((char)((tmp.u >> 32) & 0xff));
m_data.push_back((char)((tmp.u >> 24) & 0xff));
m_data.push_back((char)((tmp.u >> 16) & 0xff));
m_data.push_back((char)((tmp.u >> 8) & 0xff));
m_data.push_back((char)(tmp.u & 0xff));
}
void SimpleSerializer::writeBool(quint32 id, bool value)
{
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(!writeTag(TBool, id, 1))
return;
if(value)
m_data.push_back((char)0x01);
else m_data.push_back((char)0x00);
}
void SimpleSerializer::writeString(quint32 id, const QString& value)
{
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
QByteArray utf8 = value.toUtf8();
if(!writeTag(TString, id, utf8.size()))
return;
m_data.append(utf8);
}
void SimpleSerializer::writeBlob(quint32 id, const QByteArray& value)
{
if(id == 0) {
qCritical("SimpleSerializer: ID 0 is not allowed");
return;
}
if(!writeTag(TBlob, id, value.size()))
return;
m_data.append(value);
}
const QByteArray& SimpleSerializer::final()
{
m_finalized = true;
return m_data;
}
bool SimpleSerializer::writeTag(Type type, quint32 id, quint32 length)
{
if(m_finalized) {
qCritical("SimpleSerializer: config has already been finalized (id %u)", id);
return false;
}
int idLen;
int lengthLen;
if(id < (1 << 8))
idLen = 0;
else if(id < (1 << 16))
idLen = 1;
else if(id < (1 << 24))
idLen = 2;
else idLen = 3;
if(length < (1 << 8))
lengthLen = 0;
else if(length < (1 << 16))
lengthLen = 1;
else if(length < (1 << 24))
lengthLen = 2;
else lengthLen = 3;
m_data.push_back((char)((type << 4) | (idLen << 2) | lengthLen));
for(int i = idLen; i >= 0; i--)
m_data.push_back((char)((id >> (i * 8)) & 0xff));
for(int i = lengthLen; i >= 0; i--)
m_data.push_back((char)((length >> (i * 8)) & 0xff));
return true;
}
SimpleDeserializer::SimpleDeserializer(const QByteArray& data) :
m_data(data)
{
m_valid = parseAll();
// read version information
uint readOfs;
Elements::const_iterator it = m_elements.constFind(0);
if(it == m_elements.constEnd())
goto setInvalid;
if(it->type != TVersion)
goto setInvalid;
if(it->length > 4)
goto setInvalid;
readOfs = it->ofs;
m_version = 0;
for(uint i = 0; i < it->length; i++)
m_version = (m_version << 8) | readByte(&readOfs);
return;
setInvalid:
m_valid = false;
}
bool SimpleDeserializer::readS32(quint32 id, qint32* result, qint32 def) const
{
uint readOfs;
quint32 tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TSigned32)
goto returnDefault;
if(it->length > 4)
goto returnDefault;
readOfs = it->ofs;
tmp = 0;
for(uint i = 0; i < it->length; i++) {
quint8 byte = readByte(&readOfs);
if((i == 0) && (byte & 0x80))
tmp = 0xFFFFFFFF;
tmp = (tmp << 8) | byte;
}
*result = static_cast<qint32>(tmp);
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readU32(quint32 id, quint32* result, quint32 def) const
{
uint readOfs;
quint32 tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TUnsigned32)
goto returnDefault;
if(it->length > 4)
goto returnDefault;
readOfs = it->ofs;
tmp = 0;
for(uint i = 0; i < it->length; i++)
tmp = (tmp << 8) | readByte(&readOfs);
*result = tmp;
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readS64(quint32 id, qint64* result, qint64 def) const
{
uint readOfs;
qint64 tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TSigned64)
goto returnDefault;
if(it->length > 8)
goto returnDefault;
readOfs = it->ofs;
tmp = 0;
for(uint i = 0; i < it->length; i++) {
quint8 byte = readByte(&readOfs);
if((i == 0) && (byte & 0x80))
tmp = -1;
tmp = (tmp << 8) | byte;
}
*result = tmp;
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readU64(quint32 id, quint64* result, quint64 def) const
{
uint readOfs;
quint64 tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TUnsigned64)
goto returnDefault;
if(it->length > 8)
goto returnDefault;
readOfs = it->ofs;
tmp = 0;
for(uint i = 0; i < it->length; i++)
tmp = (tmp << 8) | readByte(&readOfs);
*result = tmp;
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readFloat(quint32 id, float* result, float def) const
{
uint readOfs;
union floatasint tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TFloat)
goto returnDefault;
if(it->length != 4)
goto returnDefault;
readOfs = it->ofs;
tmp.u = 0;
for(int i = 0; i < 4; i++)
tmp.u = (tmp.u << 8) | readByte(&readOfs);
*result = tmp.f;
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readDouble(quint32 id, double* result, double def) const
{
uint readOfs;
union doubleasint tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TDouble)
goto returnDefault;
if(it->length != 8)
goto returnDefault;
readOfs = it->ofs;
tmp.u = 0;
for(int i = 0; i < 8; i++)
tmp.u = (tmp.u << 8) | readByte(&readOfs);
*result = tmp.d;
return true;
returnDefault:
*result = def;
return false;
}
union real4asint {
quint32 u;
Real r;
};
union real8asint {
quint64 u;
Real r;
};
bool SimpleDeserializer::readReal(quint32 id, Real* result, Real def) const
{
if(sizeof(Real) == 4) {
uint readOfs;
union real4asint tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault32;
if(it->type != TFloat)
goto returnDefault32;
if(it->length != 4)
goto returnDefault32;
readOfs = it->ofs;
tmp.u = 0;
for(int i = 0; i < 4; i++)
tmp.u = (tmp.u << 8) | readByte(&readOfs);
*result = tmp.r;
return true;
returnDefault32:
*result = def;
return false;
} else {
uint readOfs;
union real8asint tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault64;
if(it->type != TDouble)
goto returnDefault64;
if(it->length != 8)
goto returnDefault64;
readOfs = it->ofs;
tmp.u = 0;
for(int i = 0; i < 8; i++)
tmp.u = (tmp.u << 8) | readByte(&readOfs);
*result = tmp.r;
return true;
returnDefault64:
*result = def;
return false;
}
}
bool SimpleDeserializer::readBool(quint32 id, bool* result, bool def) const
{
uint readOfs;
quint8 tmp;
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TBool)
goto returnDefault;
if(it->length != 1)
goto returnDefault;
readOfs = it->ofs;
tmp = readByte(&readOfs);
if(tmp == 0x00)
*result = false;
else *result = true;
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readString(quint32 id, QString* result, const QString& def) const
{
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TString)
goto returnDefault;
*result = QString::fromUtf8(m_data.data() + it->ofs, it->length);
return true;
returnDefault:
*result = def;
return false;
}
bool SimpleDeserializer::readBlob(quint32 id, QByteArray* result, const QByteArray& def) const
{
Elements::const_iterator it = m_elements.constFind(id);
if(it == m_elements.constEnd())
goto returnDefault;
if(it->type != TBlob)
goto returnDefault;
*result = QByteArray(m_data.data() + it->ofs, it->length);
return true;
returnDefault:
*result = def;
return false;
}
void SimpleDeserializer::dump() const
{
if(!m_valid) {
qDebug("SimpleDeserializer dump: data invalid");
return;
} else {
qDebug("SimpleDeserializer dump: version %u", m_version);
}
for(Elements::const_iterator it = m_elements.constBegin(); it != m_elements.constEnd(); ++it) {
switch(it->type) {
case TSigned32: {
qint32 tmp;
readS32(it.key(), &tmp);
qDebug("id %d, S32, len %d: " PRINTF_FORMAT_S32, it.key(), it->length, tmp);
break;
}
case TUnsigned32: {
quint32 tmp;
readU32(it.key(), &tmp);
qDebug("id %d, U32, len %d: " PRINTF_FORMAT_U32, it.key(), it->length, tmp);
break;
}
case TSigned64: {
qint64 tmp;
readS64(it.key(), &tmp);
// qDebug("id %d, S64, len %d: " PRINTF_FORMAT_S64, it.key(), it->length, (int)tmp);
break;
}
case TUnsigned64: {
quint64 tmp;
readU64(it.key(), &tmp);
// qDebug("id %d, U64, len %d: " PRINTF_FORMAT_U64, it.key(), it->length, (uint)tmp);
break;
}
case TFloat: {
float tmp;
readFloat(it.key(), &tmp);
qDebug("id %d, FLOAT, len %d: %f", it.key(), it->length, tmp);
break;
}
case TDouble: {
double tmp;
readDouble(it.key(), &tmp);
qDebug("id %d, DOUBLE, len %d: %f", it.key(), it->length, tmp);
break;
}
case TBool: {
bool tmp;
readBool(it.key(), &tmp);
qDebug("id %d, BOOL, len %d: %s", it.key(), it->length, tmp ? "true" : "false");
break;
}
case TString: {
QString tmp;
readString(it.key(), &tmp);
qDebug("id %d, STRING, len %d: \"%s\"", it.key(), it->length, qPrintable(tmp));
break;
}
case TBlob: {
QByteArray tmp;
readBlob(it.key(), &tmp);
qDebug("id %d, BLOB, len %d", it.key(), it->length);
break;
}
case TVersion: {
qDebug("id %d, VERSION, len %d", it.key(), it->length);
break;
}
default: {
qDebug("id %d, UNKNOWN TYPE 0x%02x, len %d", it.key(), it->type, it->length);
break;
}
}
}
}
bool SimpleDeserializer::parseAll()
{
uint readOfs = 0;
Type type;
quint32 id;
quint32 length;
/*
QString hex;
for(int i = 0; i < m_data.size(); i++)
hex.append(QString("%1 ").arg((quint8)m_data[i], 2, 16, QChar('0')));
qDebug("%s", qPrintable(hex));
qDebug("==");
*/
while(readOfs < (uint)m_data.size()) {
if(!readTag(&readOfs, m_data.size(), &type, &id, &length))
return false;
//qDebug("-- id %d, TYPE 0x%02x, len %d", id, type, length);
if(m_elements.contains(id)) {
qDebug("SimpleDeserializer: same ID found twice (id %u)", id);
return false;
}
m_elements.insert(id, Element(type, readOfs, length));
readOfs += length;
if(readOfs == (uint)m_data.size())
return true;
}
return false;
}
bool SimpleDeserializer::readTag(uint* readOfs, uint readEnd, Type* type, quint32* id, quint32* length) const
{
quint8 tag = readByte(readOfs);
*type = (Type)(tag >> 4);
int idLen = ((tag >> 2) & 0x03) + 1;
int lengthLen = (tag & 0x03) + 1;
// make sure we have enough header bytes left
if(((*readOfs) + idLen + lengthLen) > readEnd)
return false;
quint32 tmp = 0;
for(int i = 0; i < idLen; i++)
tmp = (tmp << 8) | readByte(readOfs);
*id = tmp;
tmp = 0;
for(int i = 0; i < lengthLen; i++)
tmp = (tmp << 8) | readByte(readOfs);
*length = tmp;
// check if payload fits the buffer
if(((*readOfs) + (*length)) > readEnd)
return false;
else return true;
}