diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h new file mode 100644 index 0000000000..4790fd20bb --- /dev/null +++ b/libraries/shared/src/ByteCountCoding.h @@ -0,0 +1,168 @@ +// +// ByteCountCoding.h +// libraries/shared/src +// +// Created by Brad Hefta-Gaub on 6/3/14. +// 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 +// +// +// TODO: +// * handle signed types better, check for - numeric_limits::is_signed +// * test extra long buffer with garbage data at end... + +#ifndef hifi_ByteCountCoding_h +#define hifi_ByteCountCoding_h + +#include +#include +#include +#include + +#include +#include + +#include "SharedUtil.h" + +template class ByteCountCoded { +public: + T data; + ByteCountCoded(T input = 0) : data(input) { + assert(!std::numeric_limits::is_signed); // we don't yet support signed types + }; + + ByteCountCoded(const QByteArray& fromEncoded) : data(0) { decode(fromEncoded); } + + QByteArray encode() const; + void decode(const QByteArray& fromEncoded); + + bool operator==(const ByteCountCoded& other) const { return data == other.data; } + bool operator!=(const ByteCountCoded& other) const { return data != other.data; } + bool operator!() const { return data == 0; } + + operator QByteArray() const { return encode(); }; + operator T() const { return data; }; +}; + +template inline QByteArray& operator<<(QByteArray& out, const ByteCountCoded& value) { + return out = value; +} + +template inline QByteArray& operator>>(QByteArray& in, ByteCountCoded& value) { + value.decode(in); + return in; +} + +template inline QByteArray ByteCountCoded::encode() const { + QByteArray output; + + //qDebug() << "data="; + //outputBufferBits((const unsigned char*)&data, sizeof(data)); + + T totalBits = sizeof(data) * BITS_IN_BYTE; + //qDebug() << "totalBits=" << totalBits; + T valueBits = totalBits; + bool firstValueFound = false; + T temp = data; + T lastBitMask = (T)(1) << (totalBits - 1); + + //qDebug() << "lastBitMask="; + //outputBufferBits((const unsigned char*)&lastBitMask, sizeof(lastBitMask)); + + // determine the number of bits that the value takes + for (int bitAt = 0; bitAt < totalBits; bitAt++) { + T bitValue = (temp & lastBitMask) == lastBitMask; + //qDebug() << "bitValue[" << bitAt <<"]=" << bitValue; + if (!firstValueFound) { + if (bitValue == 0) { + valueBits--; + } else { + firstValueFound = true; + } + } + temp = temp << 1; + } + qDebug() << "valueBits=" << valueBits; + + // calculate the number of total bytes, including our header + // BITS_IN_BYTE-1 because we need to code the number of bytes in the header + // + 1 because we always take at least 1 byte, even if number of bits is less than a bytes worth + int numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1; + //qDebug() << "numberOfBytes=" << numberOfBytes; + + //int numberOfBits = numberOfBytes + valueBits; + //qDebug() << "numberOfBits=" << numberOfBits; + + output.fill(0, numberOfBytes); + + // next pack the number of header bits in, the first N-1 to be set to 1, the last to be set to 0 + for(int i = 0; i < numberOfBytes; i++) { + int outputIndex = i; + T bitValue = (i < (numberOfBytes - 1) ? 1 : 0); + char original = output.at(outputIndex / BITS_IN_BYTE); + int shiftBy = BITS_IN_BYTE - ((outputIndex % BITS_IN_BYTE) + 1); + char thisBit = ( bitValue << shiftBy); + output[i / BITS_IN_BYTE] = (original | thisBit); + } + + // finally pack the the actual bits from the bit array + temp = data; + for(int i = numberOfBytes; i < (numberOfBytes + valueBits); i++) { + int outputIndex = i; + T bitValue = (temp & 1); + char original = output.at(outputIndex / BITS_IN_BYTE); + int shiftBy = BITS_IN_BYTE - ((outputIndex % BITS_IN_BYTE) + 1); + char thisBit = ( bitValue << shiftBy); + output[i / BITS_IN_BYTE] = (original | thisBit); + + temp = temp >> 1; + } + return output; +} + +template inline void ByteCountCoded::decode(const QByteArray& fromEncodedBytes) { + + // first convert the ByteArray into a BitArray... + QBitArray encodedBits; + int bitCount = BITS_IN_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_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); + } + } + + // next, read the leading bits to determine the correct number of bytes to decode (may not match the QByteArray) + int encodedByteCount = 0; + int bitAt; + for (bitAt = 0; bitAt < bitCount; bitAt++) { + if (encodedBits.at(bitAt)) { + encodedByteCount++; + } else { + break; + } + } + encodedByteCount++; // always at least one byte + int expectedBitCount = encodedByteCount * BITS_IN_BYTE; + + // Now, keep reading... + int valueStartsAt = bitAt + 1; + T value = 0; + T bitValue = 1; + for (bitAt = valueStartsAt; bitAt < expectedBitCount; bitAt++) { + if(encodedBits.at(bitAt)) { + value += bitValue; + } + bitValue *= 2; + } + data = value; +} +#endif // hifi_ByteCountCoding_h + diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h index b9253379c6..1c40e79655 100644 --- a/libraries/shared/src/PropertyFlags.h +++ b/libraries/shared/src/PropertyFlags.h @@ -11,6 +11,7 @@ // // TODO: // * consider adding iterator to enumerate the properties that have been set? +// * operator QSet - this would be easiest way to handle enumeration #ifndef hifi_PropertyFlags_h #define hifi_PropertyFlags_h @@ -33,6 +34,8 @@ public: _trailingFlipped(other._trailingFlipped) {} inline PropertyFlags(Enum flag) : _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false) { setHasProperty(flag); } + inline PropertyFlags(const QByteArray& fromEncoded) : + _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false) { decode(fromEncoded); } void clear() { _flags.clear(); _maxFlag = INT_MIN; _minFlag = INT_MAX; _trailingFlipped = false; } @@ -41,9 +44,11 @@ public: void setHasProperty(Enum flag, bool value = true); bool getHasProperty(Enum flag); - QByteArray encode(); + QByteArray encode() const; void decode(const QByteArray& fromEncoded); + operator QByteArray() const { return encode(); }; + bool operator==(const PropertyFlags& other) const { return _flags == other._flags; } bool operator!=(const PropertyFlags& other) const { return _flags != other._flags; } @@ -142,7 +147,7 @@ template inline bool PropertyFlags::getHasProperty(Enum fla const int BITS_PER_BYTE = 8; -template inline QByteArray PropertyFlags::encode() { +template inline QByteArray PropertyFlags::encode() const { QByteArray output; if (_maxFlag < _minFlag) { @@ -414,5 +419,14 @@ template inline void PropertyFlags::shinkIfNeeded() { } } +template inline QByteArray& operator<<(QByteArray& out, const PropertyFlags& value) { + return out = value; +} + +template inline QByteArray& operator>>(QByteArray& in, PropertyFlags& value) { + value.decode(in); + return in; +} + #endif // hifi_PropertyFlags_h