mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-13 15:14:25 +02:00
Removing heap allocation from property flag parsing, adding some manual tests
This commit is contained in:
parent
200cbd7e8d
commit
2ce8dba819
5 changed files with 231 additions and 43 deletions
|
@ -44,10 +44,7 @@ public:
|
|||
|
||||
template <typename T>
|
||||
inline void readFlags(PropertyFlags<T>& result) {
|
||||
// FIXME doing heap allocation
|
||||
QByteArray encoded((const char*)(_data + _offset), remaining());
|
||||
result.decode(encoded);
|
||||
_offset += result.getEncodedLength();
|
||||
_offset += result.decode(_data + _offset, remaining());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QBitArray>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "ByteCountCoding.h"
|
||||
#include <SharedUtil.h>
|
||||
|
||||
template<typename Enum>class PropertyFlags {
|
||||
|
@ -51,7 +52,8 @@ public:
|
|||
void setHasProperty(Enum flag, bool value = true);
|
||||
bool getHasProperty(Enum flag) const;
|
||||
QByteArray encode();
|
||||
void decode(const QByteArray& fromEncoded);
|
||||
size_t decode(const uint8_t* data, size_t length);
|
||||
size_t decode(const QByteArray& fromEncoded);
|
||||
|
||||
operator QByteArray() { return encode(); };
|
||||
|
||||
|
@ -193,51 +195,63 @@ template<typename Enum> inline QByteArray PropertyFlags<Enum>::encode() {
|
|||
return output;
|
||||
}
|
||||
|
||||
template<typename Enum> inline void PropertyFlags<Enum>::decode(const QByteArray& fromEncodedBytes) {
|
||||
template<typename Enum>
|
||||
inline size_t PropertyFlags<Enum>::decode(const uint8_t* data, size_t size) {
|
||||
clear();
|
||||
//clear(); // we are cleared out!
|
||||
|
||||
clear(); // we are cleared out!
|
||||
size_t bytesConsumed = 0;
|
||||
int bitCount = BITS_IN_BYTE * size;
|
||||
|
||||
// first convert the ByteArray into a BitArray...
|
||||
QBitArray encodedBits;
|
||||
int bitCount = BITS_PER_BYTE * fromEncodedBytes.count();
|
||||
encodedBits.resize(bitCount);
|
||||
|
||||
for(int byte = 0; byte < fromEncodedBytes.count(); byte++) {
|
||||
char originalByte = fromEncodedBytes.at(byte);
|
||||
for(int bit = 0; bit < BITS_PER_BYTE; bit++) {
|
||||
int shiftBy = BITS_PER_BYTE - (bit + 1);
|
||||
char maskBit = ( 1 << shiftBy);
|
||||
bool bitValue = originalByte & maskBit;
|
||||
encodedBits.setBit(byte * BITS_PER_BYTE + bit, bitValue);
|
||||
int encodedByteCount = 1; // there is at least 1 byte (after the leadBits)
|
||||
int leadBits = 1; // there is always at least 1 lead bit
|
||||
bool inLeadBits = true;
|
||||
int bitAt = 0;
|
||||
int expectedBitCount; // unknown at this point
|
||||
int lastValueBit;
|
||||
for (int byte = 0; byte < size; byte++) {
|
||||
char originalByte = data[byte];
|
||||
bytesConsumed++;
|
||||
unsigned char maskBit = 0x80; // LEFT MOST BIT set
|
||||
for (int bit = 0; bit < BITS_IN_BYTE; bit++) {
|
||||
bool bitIsSet = originalByte & maskBit;
|
||||
// Processing of the lead bits
|
||||
if (inLeadBits) {
|
||||
if (bitIsSet) {
|
||||
encodedByteCount++;
|
||||
leadBits++;
|
||||
} else {
|
||||
inLeadBits = false; // once we hit our first 0, we know we're out of the lead bits
|
||||
expectedBitCount = (encodedByteCount * BITS_IN_BYTE) - leadBits;
|
||||
lastValueBit = expectedBitCount + bitAt;
|
||||
|
||||
// check to see if the remainder of our buffer is sufficient
|
||||
if (expectedBitCount > (bitCount - leadBits)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (bitAt > lastValueBit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bitIsSet) {
|
||||
setHasProperty(static_cast<Enum>(bitAt - leadBits), true);
|
||||
}
|
||||
}
|
||||
bitAt++;
|
||||
maskBit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray)
|
||||
int encodedByteCount = 0;
|
||||
int leadBits = 1;
|
||||
int bitAt;
|
||||
for (bitAt = 0; bitAt < bitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
encodedByteCount++;
|
||||
leadBits++;
|
||||
} else {
|
||||
if (!inLeadBits && bitAt > lastValueBit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
encodedByteCount++; // always at least one byte
|
||||
_encodedLength = encodedByteCount;
|
||||
_encodedLength = bytesConsumed;
|
||||
return bytesConsumed;
|
||||
}
|
||||
|
||||
int expectedBitCount = encodedByteCount * BITS_PER_BYTE;
|
||||
|
||||
// Now, keep reading...
|
||||
if (expectedBitCount <= (encodedBits.size() - leadBits)) {
|
||||
int flagsStartAt = bitAt + 1;
|
||||
for (bitAt = flagsStartAt; bitAt < expectedBitCount; bitAt++) {
|
||||
if (encodedBits.at(bitAt)) {
|
||||
setHasProperty((Enum)(bitAt - flagsStartAt));
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename Enum> inline size_t PropertyFlags<Enum>::decode(const QByteArray& fromEncodedBytes) {
|
||||
return decode(reinterpret_cast<const uint8_t*>(fromEncodedBytes.data()), fromEncodedBytes.size());
|
||||
}
|
||||
|
||||
template<typename Enum> inline void PropertyFlags<Enum>::debugDumpBits() {
|
||||
|
|
12
tests/entities/CMakeLists.txt
Normal file
12
tests/entities/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
set(TARGET_NAME "entities-test")
|
||||
|
||||
# This is not a testcase -- just set it up as a regular hifi project
|
||||
setup_hifi_project()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(entities avatars shared octree gpu model fbx networking animation environment)
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
BIN
tests/entities/packet.bin
Normal file
BIN
tests/entities/packet.bin
Normal file
Binary file not shown.
165
tests/entities/src/main.cpp
Normal file
165
tests/entities/src/main.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// main.cpp
|
||||
// tests/render-utils/src
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QLoggingCategory>
|
||||
#include <QDir>
|
||||
#include <ByteCountCoding.h>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <BoxEntityItem.h>
|
||||
#include <Octree.h>
|
||||
|
||||
const QString& getTestResourceDir() {
|
||||
static QString dir;
|
||||
if (dir.isEmpty()) {
|
||||
QDir path(__FILE__);
|
||||
path.cdUp();
|
||||
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
||||
qDebug() << "Qml Test Path: " << dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
class StopWatch {
|
||||
public:
|
||||
void start() {
|
||||
Q_ASSERT(_start == 0);
|
||||
_start = usecTimestampNow();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
Q_ASSERT(_start != 0);
|
||||
_last = usecTimestampNow() - _start;
|
||||
_start = 0;
|
||||
_total += _last;
|
||||
_count++;
|
||||
}
|
||||
|
||||
quint64 getLast() {
|
||||
return _last;
|
||||
}
|
||||
|
||||
quint64 getTotal() {
|
||||
return _total;
|
||||
}
|
||||
|
||||
float getAverage() {
|
||||
return (float)_total / (float)_count;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_last = _start = _total = _count = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _count{ 0 };
|
||||
quint64 _total{ 0 };
|
||||
quint64 _start{ 0 };
|
||||
quint64 _last{ 0 };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void testByteCountCodedStable(const T& value) {
|
||||
ByteCountCoded<T> coder((T)value);
|
||||
auto encoded = coder.encode();
|
||||
auto originalEncodedSize = encoded.size();
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
encoded.append(qrand());
|
||||
}
|
||||
ByteCountCoded<T> decoder;
|
||||
decoder.decode(encoded);
|
||||
Q_ASSERT(decoder.data == coder.data);
|
||||
auto consumed = decoder.decode(encoded.data(), encoded.size());
|
||||
Q_ASSERT(consumed == originalEncodedSize);
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void testByteCountCoded() {
|
||||
testByteCountCodedStable<T>(0);
|
||||
testByteCountCodedStable<T>(1);
|
||||
testByteCountCodedStable<T>(1 << 16);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 16);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 8);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max() >> 1);
|
||||
testByteCountCodedStable<T>(std::numeric_limits<T>::max());
|
||||
}
|
||||
|
||||
void testPropertyFlags(uint32_t value) {
|
||||
EntityPropertyFlags original;
|
||||
original.clear();
|
||||
auto enumSize = sizeof(EntityPropertyList);
|
||||
for (size_t i = 0; i < sizeof(EntityPropertyList) * 8; ++i) {
|
||||
original.setHasProperty((EntityPropertyList)i);
|
||||
}
|
||||
QByteArray encoded = original.encode();
|
||||
auto originalSize = encoded.size();
|
||||
for (size_t i = 0; i < sizeof(EntityPropertyList); ++i) {
|
||||
encoded.append(qrand());
|
||||
}
|
||||
|
||||
EntityPropertyFlags decodeOld, decodeNew;
|
||||
{
|
||||
decodeOld.decode(encoded);
|
||||
Q_ASSERT(decodeOld == original);
|
||||
}
|
||||
|
||||
{
|
||||
auto decodeSize = decodeNew.decode((const uint8_t*)encoded.data(), encoded.size());
|
||||
Q_ASSERT(originalSize == decodeSize);
|
||||
Q_ASSERT(decodeNew == original);
|
||||
}
|
||||
}
|
||||
|
||||
void testPropertyFlags() {
|
||||
testPropertyFlags(0);
|
||||
testPropertyFlags(1);
|
||||
testPropertyFlags(1 << 16);
|
||||
testPropertyFlags(0xFFFF);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QCoreApplication app(argc, argv);
|
||||
{
|
||||
auto start = usecTimestampNow();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
testPropertyFlags();
|
||||
testByteCountCoded<quint8>();
|
||||
testByteCountCoded<quint16>();
|
||||
testByteCountCoded<quint32>();
|
||||
testByteCountCoded<quint64>();
|
||||
}
|
||||
auto duration = usecTimestampNow() - start;
|
||||
qDebug() << duration;
|
||||
|
||||
}
|
||||
DependencyManager::set<NodeList>(NodeType::Unassigned);
|
||||
|
||||
QFile file(getTestResourceDir() + "packet.bin");
|
||||
if (!file.open(QIODevice::ReadOnly)) return -1;
|
||||
QByteArray packet = file.readAll();
|
||||
EntityItemPointer item = BoxEntityItem::factory(EntityItemID(), EntityItemProperties());
|
||||
ReadBitstreamToTreeParams params;
|
||||
params.bitstreamVersion = 33;
|
||||
|
||||
auto start = usecTimestampNow();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
item->readEntityDataFromBuffer(reinterpret_cast<const unsigned char*>(packet.constData()), packet.size(), params);
|
||||
}
|
||||
float duration = (usecTimestampNow() - start);
|
||||
qDebug() << (duration / 1000.0f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "main.moc"
|
Loading…
Reference in a new issue