sdrangel/sdrbase/util/simpleserializer.cpp

727 lines
15 KiB
C++

#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;
qint32 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 = -1;
tmp = (tmp << 8) | byte;
}
*result = 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;
}