diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 48fa53777d..a834ce1288 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1014,6 +1014,7 @@ void Application::paintGL() { void Application::runTests() { runTimingTests(); + runUnitTests(); } void Application::audioMuteToggled() { diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index bf4df3f3d2..82f7d55b5d 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -21,6 +21,7 @@ #include +#include #include #include @@ -229,6 +230,43 @@ void runTimingTests() { elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; qCDebug(interfaceapp, "vec3 assign and dot() usecs: %f, last result:%f", (double)(elapsedUsecs / numTests), (double)result); + + + quint64 BYTE_CODE_MAX_TEST_VALUE = 99999999; + quint64 BYTE_CODE_TESTS_SKIP = 999; + + QByteArray extraJunk; + const int EXTRA_JUNK_SIZE = 200; + extraJunk.append((unsigned char)255); + for (int i = 0; i < EXTRA_JUNK_SIZE; i++) { + extraJunk.append(QString("junk")); + } + + { + startTime.start(); + quint64 tests = 0; + quint64 failed = 0; + for (quint64 value = 0; value < BYTE_CODE_MAX_TEST_VALUE; value += BYTE_CODE_TESTS_SKIP) { + quint64 valueA = value; // usecTimestampNow(); + ByteCountCoded codedValueA = valueA; + QByteArray codedValueABuffer = codedValueA; + codedValueABuffer.append(extraJunk); + ByteCountCoded decodedValueA; + decodedValueA.decode(codedValueABuffer); + quint64 valueADecoded = decodedValueA; + tests++; + if (valueA != valueADecoded) { + qDebug() << "FAILED! value:" << valueA << "decoded:" << valueADecoded; + failed++; + } + + } + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qCDebug(interfaceapp) << "ByteCountCoded usecs: " << elapsedUsecs + << "per test:" << (double) (elapsedUsecs / tests) + << "tests:" << tests + << "failed:" << failed; + } } bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, @@ -271,3 +309,39 @@ bool pointInSphere(glm::vec3& point, glm::vec3& sphereCenter, double sphereRadiu } return false; } + +void runUnitTests() { + + quint64 LAST_TEST = 10; + quint64 SKIP_BY = 1; + + for (quint64 value = 0; value <= LAST_TEST; value += SKIP_BY) { + qDebug() << "value:" << value; + + ByteCountCoded codedValue = value; + + QByteArray codedValueBuffer = codedValue; + + codedValueBuffer.append((unsigned char)255); + codedValueBuffer.append(QString("junk")); + + qDebug() << "codedValueBuffer:"; + outputBufferBits((const unsigned char*)codedValueBuffer.constData(), codedValueBuffer.size()); + + ByteCountCoded valueDecoder; + size_t bytesConsumed = valueDecoder.decode(codedValueBuffer); + quint64 valueDecoded = valueDecoder; + qDebug() << "valueDecoded:" << valueDecoded; + qDebug() << "bytesConsumed:" << bytesConsumed; + + + if (value == valueDecoded) { + qDebug() << "SUCCESS!"; + } else { + qDebug() << "FAILED!"; + } + + } +} + + diff --git a/interface/src/Util.h b/interface/src/Util.h index ed05209747..d252c26bef 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -30,6 +30,7 @@ void drawText(int x, int y, float scale, float radians, int mono, void renderCollisionOverlay(int width, int height, float magnitude, float red = 0, float blue = 0, float green = 0); void runTimingTests(); +void runUnitTests(); bool rayIntersectsSphere(const glm::vec3& rayStarting, const glm::vec3& rayNormalizedDirection, const glm::vec3& sphereCenter, float sphereRadius, float& distance); diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h index 2a39ee7a8c..ce6f121ddb 100644 --- a/libraries/shared/src/ByteCountCoding.h +++ b/libraries/shared/src/ByteCountCoding.h @@ -21,9 +21,13 @@ #include #include +#include + #include #include +#include "SharedUtil.h" + #include "NumericalConstants.h" template class ByteCountCoded { @@ -37,7 +41,8 @@ public: ByteCountCoded(const QByteArray& fromEncoded) : data(0) { decode(fromEncoded); } QByteArray encode() const; - void decode(const QByteArray& fromEncoded); + size_t decode(const QByteArray& fromEncoded); + size_t decode(const char* encodedBuffer, int encodedSize); bool operator==(const ByteCountCoded& other) const { return data == other.data; } bool operator!=(const ByteCountCoded& other) const { return data != other.data; } @@ -110,52 +115,63 @@ template inline QByteArray ByteCountCoded::encode() const { return output; } -template inline void ByteCountCoded::decode(const QByteArray& fromEncodedBytes) { +template inline size_t ByteCountCoded::decode(const QByteArray& fromEncodedBytes) { + return decode(fromEncodedBytes.constData(), fromEncodedBytes.size()); +} - // first convert the ByteArray into a BitArray... - QBitArray encodedBits; - int bitCount = BITS_IN_BYTE * fromEncodedBytes.count(); - encodedBits.resize(bitCount); +template inline size_t ByteCountCoded::decode(const char* encodedBuffer, int encodedSize) { + data = 0; // reset data + size_t bytesConsumed = 0; + int bitCount = BITS_IN_BYTE * encodedSize; + + 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; + T bitValue = 1; - for(int byte = 0; byte < fromEncodedBytes.count(); byte++) { - char originalByte = fromEncodedBytes.at(byte); + for(int byte = 0; byte < encodedSize; byte++) { + char originalByte = encodedBuffer[byte]; + bytesConsumed++; + unsigned char maskBit = 128; // LEFT MOST BIT set for(int bit = 0; bit < BITS_IN_BYTE; bit++) { - int shiftBy = BITS_IN_BYTE - (bit + 1); - char maskBit = ( 1 << shiftBy); - bool bitValue = originalByte & maskBit; - encodedBits.setBit(byte * BITS_IN_BYTE + bit, bitValue); + 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) { + data += bitValue; + } + bitValue *= 2; + } + bitAt++; + maskBit = 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 - int expectedBitCount = encodedByteCount * BITS_IN_BYTE; - - T value = 0; - - if (expectedBitCount <= (encodedBits.size() - leadBits)) { - // Now, keep reading... - int valueStartsAt = bitAt + 1; - T bitValue = 1; - for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) { - if(encodedBits.at(bitAt)) { - value += bitValue; - } - bitValue *= 2; - } - } - data = value; + return bytesConsumed; } #endif // hifi_ByteCountCoding_h