mumble/src/PacketDataStream.h
2020-01-07 03:09:22 +01:00

427 lines
8.5 KiB
C++

// Copyright 2005-2020 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_
/*
* 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 {
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]);
}
const char *charPtr() const {
return reinterpret_cast<const char *>(& data[offset]);
}
QByteArray dataBlock(quint32 len) {
if (len <= left()) {
QByteArray a(charPtr(), len);
offset +=len;
return a;
} else {
ok = false;
return QByteArray();
}
}
protected:
void setup(unsigned char *d, int msize) {
data = d;
offset = 0;
overshoot = 0;
maxsize = msize;
ok = true;
}
public:
PacketDataStream(const char *d, int msize) {
setup(const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(d)), msize);
};
PacketDataStream(char *d, int msize) {
setup(reinterpret_cast<unsigned char *>(d), msize);
};
PacketDataStream(unsigned char *d, int msize) {
setup(d, msize);
};
PacketDataStream(const QByteArray &qba) {
setup(const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(qba.constData())), qba.size());
}
PacketDataStream(QByteArray &qba) {
unsigned char *ptr=reinterpret_cast<unsigned char *>(qba.data());
setup(ptr, qba.capacity());
}
PacketDataStream &operator <<(const quint64 value) {
quint64 i = value;
if ((i & 0x8000000000000000LL) && (~i < 0x100000000LL)) {
// Signed number.
i = ~i;
if (i <= 0x3) {
// Shortcase for -1 to -4
append(0xFC | i);
return *this;
} else {
append(0xF8);
}
}
if (i < 0x80) {
// Need top bit clear
append(i);
} else if (i < 0x4000) {
// Need top two bits clear
append((i >> 8) | 0x80);
append(i & 0xFF);
} else if (i < 0x200000) {
// Need top three bits clear
append((i >> 16) | 0xC0);
append((i >> 8) & 0xFF);
append(i & 0xFF);
} else if (i < 0x10000000) {
// Need top four bits clear
append((i >> 24) | 0xE0);
append((i >> 16) & 0xFF);
append((i >> 8) & 0xFF);
append(i & 0xFF);
} else if (i < 0x100000000LL) {
// It's a full 32-bit integer.
append(0xF0);
append((i >> 24) & 0xFF);
append((i >> 16) & 0xFF);
append((i >> 8) & 0xFF);
append(i & 0xFF);
} else {
// It's a 64-bit value.
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(), 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]), 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]), 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