414 lines
10 KiB
C++
414 lines
10 KiB
C++
// Copyright 2009-2023 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#ifndef MUMBLE_PACKETDATASTREAM_H_
|
|
#define MUMBLE_PACKETDATASTREAM_H_
|
|
|
|
#include <QByteArray>
|
|
#include <QPair>
|
|
#include <QString>
|
|
|
|
#include <cstring>
|
|
|
|
/*
|
|
* GCC doesn't yet do inter-object-file inlining, so unfortunately, this all has to be defined here.
|
|
*/
|
|
|
|
class PacketDataStream {
|
|
private:
|
|
Q_DISABLE_COPY(PacketDataStream)
|
|
private:
|
|
unsigned char *data;
|
|
quint32 maxsize;
|
|
quint32 offset;
|
|
quint32 overshoot;
|
|
bool ok;
|
|
|
|
public:
|
|
quint32 size() const { return offset; }
|
|
|
|
quint32 capacity() const { return maxsize; }
|
|
|
|
bool isValid() const { return ok; }
|
|
|
|
quint32 left() const { return maxsize - offset; }
|
|
|
|
quint32 undersize() const { return overshoot; }
|
|
|
|
void append(const quint64 v) {
|
|
#ifndef QT_NO_DEBUG
|
|
Q_ASSERT(v <= 0xff);
|
|
#endif
|
|
if (offset < maxsize)
|
|
data[offset++] = static_cast< unsigned char >(v);
|
|
else {
|
|
ok = false;
|
|
overshoot++;
|
|
}
|
|
};
|
|
|
|
void append(const char *d, quint32 len) {
|
|
if (left() >= len) {
|
|
memcpy(&data[offset], d, len);
|
|
offset += len;
|
|
} else {
|
|
unsigned int l = left();
|
|
memset(&data[offset], 0, l);
|
|
offset += l;
|
|
overshoot += len - l;
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
void skip(quint32 len) {
|
|
if (left() >= len)
|
|
offset += len;
|
|
else
|
|
ok = false;
|
|
}
|
|
|
|
quint64 next() {
|
|
if (offset < maxsize)
|
|
return data[offset++];
|
|
else {
|
|
ok = false;
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
quint8 next8() {
|
|
if (offset < maxsize)
|
|
return data[offset++];
|
|
else {
|
|
ok = false;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void rewind() { offset = 0; }
|
|
|
|
void truncate() { maxsize = offset; }
|
|
|
|
const unsigned char *dataPtr() const { return reinterpret_cast< const unsigned char * >(&data[offset]); }
|
|
|
|
unsigned char *dataPtr() { return reinterpret_cast< unsigned char * >(&data[offset]); }
|
|
|
|
const char *charPtr() const { return reinterpret_cast< const char * >(&data[offset]); }
|
|
|
|
QByteArray dataBlock(quint32 len) {
|
|
if (len <= left()) {
|
|
QByteArray a(charPtr(), static_cast< int >(len));
|
|
offset += len;
|
|
return a;
|
|
} else {
|
|
ok = false;
|
|
return QByteArray();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void setup(unsigned char *d, unsigned int msize) {
|
|
data = d;
|
|
offset = 0;
|
|
overshoot = 0;
|
|
maxsize = msize;
|
|
ok = true;
|
|
}
|
|
|
|
public:
|
|
PacketDataStream(const unsigned char *d, unsigned int msize) { setup(const_cast< unsigned char * >(d), msize); };
|
|
|
|
PacketDataStream(const char *d, unsigned int msize) {
|
|
setup(const_cast< unsigned char * >(reinterpret_cast< const unsigned char * >(d)), msize);
|
|
};
|
|
|
|
PacketDataStream(char *d, unsigned int msize) { setup(reinterpret_cast< unsigned char * >(d), msize); };
|
|
|
|
PacketDataStream(unsigned char *d, unsigned int msize) { setup(d, msize); };
|
|
|
|
PacketDataStream(const QByteArray &qba) {
|
|
setup(const_cast< unsigned char * >(reinterpret_cast< const unsigned char * >(qba.constData())),
|
|
static_cast< unsigned int >(qba.size()));
|
|
}
|
|
|
|
PacketDataStream(QByteArray &qba) {
|
|
unsigned char *ptr = reinterpret_cast< unsigned char * >(qba.data());
|
|
setup(ptr, static_cast< unsigned int >(qba.capacity()));
|
|
}
|
|
|
|
PacketDataStream &operator<<(const quint64 value) {
|
|
quint64 i = value;
|
|
|
|
if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) {
|
|
// Signed number.
|
|
i = ~i;
|
|
if (i <= 0x3) {
|
|
// Special case for -1 to -4. The most significant bits of the first byte must be (in binary) 111111
|
|
// followed by the 2 bits representing the absolute value of the encoded number. Shortcase for -1 to -4
|
|
append(0xFC | i);
|
|
return *this;
|
|
} else {
|
|
// Add flag byte, whose most significant bits are (in binary) 111110 that indicates
|
|
// that what follows is the varint encoding of the absolute value of i, but that the
|
|
// value itself is supposed to be negative.
|
|
append(0xF8);
|
|
}
|
|
}
|
|
if (i < 0x80) {
|
|
// Encode as 7-bit, positive number -> most significant bit of first byte must be zero
|
|
append(i);
|
|
} else if (i < 0x4000) {
|
|
// Encode as 14-bit, positive number -> most significant bits of first byte must be (in binary) 10
|
|
append((i >> 8) | 0x80);
|
|
append(i & 0xFF);
|
|
} else if (i < 0x200000) {
|
|
// Encode as 21-bit, positive number -> most significant bits of first byte must be (in binary) 110
|
|
append((i >> 16) | 0xC0);
|
|
append((i >> 8) & 0xFF);
|
|
append(i & 0xFF);
|
|
} else if (i < 0x10000000) {
|
|
// Encode as 28-bit, positive number -> most significant bits of first byte must be (in binary) 1110
|
|
append((i >> 24) | 0xE0);
|
|
append((i >> 16) & 0xFF);
|
|
append((i >> 8) & 0xFF);
|
|
append(i & 0xFF);
|
|
} else if (i < 0x100000000LL) {
|
|
// Encode as 32-bit, positive number -> most significant bits of first byte must be (in binary) 111100
|
|
// Remaining bits in first byte remain unused
|
|
append(0xF0);
|
|
append((i >> 24) & 0xFF);
|
|
append((i >> 16) & 0xFF);
|
|
append((i >> 8) & 0xFF);
|
|
append(i & 0xFF);
|
|
} else {
|
|
// Encode as 64-bit, positive number -> most significant bits of first byte must be (in binary) 111101
|
|
// Remaining bits in first byte remain unused
|
|
append(0xF4);
|
|
append((i >> 56) & 0xFF);
|
|
append((i >> 48) & 0xFF);
|
|
append((i >> 40) & 0xFF);
|
|
append((i >> 32) & 0xFF);
|
|
append((i >> 24) & 0xFF);
|
|
append((i >> 16) & 0xFF);
|
|
append((i >> 8) & 0xFF);
|
|
append(i & 0xFF);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator>>(quint64 &i) {
|
|
quint64 v = next();
|
|
|
|
if ((v & 0x80) == 0x00) {
|
|
i = (v & 0x7F);
|
|
} else if ((v & 0xC0) == 0x80) {
|
|
i = (v & 0x3F) << 8 | next();
|
|
} else if ((v & 0xF0) == 0xF0) {
|
|
switch (v & 0xFC) {
|
|
case 0xF0:
|
|
i = next() << 24 | next() << 16 | next() << 8 | next();
|
|
break;
|
|
case 0xF4:
|
|
i = next() << 56 | next() << 48 | next() << 40 | next() << 32 | next() << 24 | next() << 16
|
|
| next() << 8 | next();
|
|
break;
|
|
case 0xF8:
|
|
*this >> i;
|
|
i = ~i;
|
|
break;
|
|
case 0xFC:
|
|
i = v & 0x03;
|
|
i = ~i;
|
|
break;
|
|
default:
|
|
ok = false;
|
|
i = 0;
|
|
break;
|
|
}
|
|
} else if ((v & 0xF0) == 0xE0) {
|
|
i = (v & 0x0F) << 24 | next() << 16 | next() << 8 | next();
|
|
} else if ((v & 0xE0) == 0xC0) {
|
|
i = (v & 0x1F) << 16 | next() << 8 | next();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator<<(const QByteArray &a) {
|
|
*this << a.size();
|
|
append(a.constData(), static_cast< unsigned int >(a.size()));
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator>>(QByteArray &a) {
|
|
quint32 len;
|
|
*this >> len;
|
|
if (len > left()) {
|
|
len = left();
|
|
ok = false;
|
|
}
|
|
a = QByteArray(reinterpret_cast< const char * >(&data[offset]), static_cast< int >(len));
|
|
offset += len;
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator<<(const QString &s) { return *this << s.toUtf8(); }
|
|
|
|
// Using the data directly instead of through qbuff avoids a copy.
|
|
PacketDataStream &operator>>(QString &s) {
|
|
quint32 len;
|
|
*this >> len;
|
|
if (len > left()) {
|
|
len = left();
|
|
ok = false;
|
|
}
|
|
s = QString::fromUtf8(reinterpret_cast< const char * >(&data[offset]), static_cast< int >(len));
|
|
offset += len;
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator<<(const bool b) {
|
|
quint32 v = b ? 1 : 0;
|
|
return *this << v;
|
|
}
|
|
|
|
PacketDataStream &operator>>(bool &b) {
|
|
quint32 v;
|
|
*this >> v;
|
|
b = v ? true : false;
|
|
return *this;
|
|
}
|
|
|
|
#define INTMAPOPERATOR(type) \
|
|
PacketDataStream &operator<<(const type v) { return *this << static_cast< quint64 >(v); } \
|
|
PacketDataStream &operator>>(type &v) { \
|
|
quint64 vv; \
|
|
*this >> vv; \
|
|
v = static_cast< type >(vv); \
|
|
return *this; \
|
|
}
|
|
|
|
|
|
INTMAPOPERATOR(int);
|
|
INTMAPOPERATOR(unsigned int);
|
|
INTMAPOPERATOR(short);
|
|
INTMAPOPERATOR(unsigned short);
|
|
INTMAPOPERATOR(char);
|
|
INTMAPOPERATOR(unsigned char);
|
|
|
|
union double64u {
|
|
quint64 ui;
|
|
double d;
|
|
};
|
|
|
|
PacketDataStream &operator<<(const double v) {
|
|
double64u u;
|
|
u.d = v;
|
|
return *this << u.ui;
|
|
}
|
|
|
|
PacketDataStream &operator>>(double &v) {
|
|
double64u u;
|
|
*this >> u.ui;
|
|
v = u.d;
|
|
return *this;
|
|
}
|
|
|
|
union float32u {
|
|
quint8 ui[4];
|
|
float f;
|
|
};
|
|
|
|
PacketDataStream &operator<<(const float v) {
|
|
float32u u;
|
|
u.f = v;
|
|
append(u.ui[0]);
|
|
append(u.ui[1]);
|
|
append(u.ui[2]);
|
|
append(u.ui[3]);
|
|
return *this;
|
|
}
|
|
|
|
PacketDataStream &operator>>(float &v) {
|
|
float32u u;
|
|
if (left() < 4) {
|
|
ok = false;
|
|
v = 0;
|
|
}
|
|
u.ui[0] = next8();
|
|
u.ui[1] = next8();
|
|
u.ui[2] = next8();
|
|
u.ui[3] = next8();
|
|
v = u.f;
|
|
return *this;
|
|
}
|
|
|
|
template< typename T > PacketDataStream &operator<<(const QList< T > &l) {
|
|
*this << l.size();
|
|
for (int i = 0; i < l.size(); i++)
|
|
*this << l.at(i);
|
|
return *this;
|
|
}
|
|
|
|
template< typename T > PacketDataStream &operator>>(QList< T > &l) {
|
|
l.clear();
|
|
quint32 len;
|
|
*this >> len;
|
|
if (len > left()) {
|
|
len = left();
|
|
ok = false;
|
|
}
|
|
for (quint32 i = 0; i < len; i++) {
|
|
if (left() == 0) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
|
|
T t;
|
|
*this >> t;
|
|
l.append(t);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
template< typename T > PacketDataStream &operator<<(const QSet< T > &s) {
|
|
*this << s.size();
|
|
for (typename QSet< T >::const_iterator i = s.constBegin(); i != s.constEnd(); ++i)
|
|
*this << *i;
|
|
return *this;
|
|
}
|
|
|
|
template< typename T > PacketDataStream &operator>>(QSet< T > &s) {
|
|
s.clear();
|
|
quint32 len;
|
|
*this >> len;
|
|
if (len > left()) {
|
|
len = left();
|
|
ok = false;
|
|
}
|
|
for (quint32 i = 0; i < len; i++) {
|
|
if (left() == 0) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
|
|
T t;
|
|
*this >> t;
|
|
s.insert(t);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template< typename T, typename U > PacketDataStream &operator<<(const QPair< T, U > &p) {
|
|
return *this << p.first << p.second;
|
|
}
|
|
|
|
template< typename T, typename U > PacketDataStream &operator>>(QPair< T, U > &p) {
|
|
return *this >> p.first >> p.second;
|
|
}
|
|
};
|
|
|
|
#endif
|