// // PropertyFlags.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: // * consider adding iterator to enumerate the properties that have been set? // * operator QSet - this would be easiest way to handle enumeration // * make encode(), QByteArray<< operator, and QByteArray operator const by moving calculation of encoded length to // setFlag() and other calls // * should the QByteArray<< operator and QByteArray>> operator return the shifted versions of the byte arrays? #ifndef hifi_PropertyFlags_h #define hifi_PropertyFlags_h #include #include #include #include #include "ByteCountCoding.h" #include "SharedLogging.h" templateclass PropertyFlags { public: typedef Enum enum_type; inline PropertyFlags() : _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { }; inline PropertyFlags(const PropertyFlags& other) : _flags(other._flags), _maxFlag(other._maxFlag), _minFlag(other._minFlag), _trailingFlipped(other._trailingFlipped), _encodedLength(0) {} inline PropertyFlags(Enum flag) : _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { setHasProperty(flag); } inline PropertyFlags(const QByteArray& fromEncoded) : _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { decode(fromEncoded); } void clear() { _flags.clear(); _maxFlag = INT_MIN; _minFlag = INT_MAX; _trailingFlipped = false; _encodedLength = 0; } bool isEmpty() const { return _maxFlag == INT_MIN && _minFlag == INT_MAX && _trailingFlipped == false && _encodedLength == 0; } Enum firstFlag() const { return (Enum)_minFlag; } Enum lastFlag() const { return (Enum)_maxFlag; } void setHasProperty(Enum flag, bool value = true); bool getHasProperty(Enum flag) const; QByteArray encode(); size_t decode(const uint8_t* data, size_t length); size_t decode(const QByteArray& fromEncoded); operator QByteArray() { return encode(); }; bool operator==(const PropertyFlags& other) const { return _flags == other._flags; } bool operator!=(const PropertyFlags& other) const { return _flags != other._flags; } bool operator!() const { return _flags.size() == 0; } PropertyFlags& operator=(const PropertyFlags& other); PropertyFlags& operator|=(const PropertyFlags& other); PropertyFlags& operator|=(Enum flag); PropertyFlags& operator&=(const PropertyFlags& other); PropertyFlags& operator&=(Enum flag); PropertyFlags& operator+=(const PropertyFlags& other); PropertyFlags& operator+=(Enum flag); PropertyFlags& operator-=(const PropertyFlags& other); PropertyFlags& operator-=(Enum flag); PropertyFlags& operator<<=(const PropertyFlags& other); PropertyFlags& operator<<=(Enum flag); PropertyFlags operator|(const PropertyFlags& other) const; PropertyFlags operator|(Enum flag) const; PropertyFlags operator&(const PropertyFlags& other) const; PropertyFlags operator&(Enum flag) const; PropertyFlags operator+(const PropertyFlags& other) const; PropertyFlags operator+(Enum flag) const; PropertyFlags operator-(const PropertyFlags& other) const; PropertyFlags operator-(Enum flag) const; PropertyFlags operator<<(const PropertyFlags& other) const; PropertyFlags operator<<(Enum flag) const; // NOTE: due to the nature of the compact storage of these property flags, and the fact that the upper bound of the // enum is not known, these operators will only perform their bitwise operations on the set of properties that have // been previously set PropertyFlags& operator^=(const PropertyFlags& other); PropertyFlags& operator^=(Enum flag); PropertyFlags operator^(const PropertyFlags& other) const; PropertyFlags operator^(Enum flag) const; PropertyFlags operator~() const; void debugDumpBits(); int getEncodedLength() const { return _encodedLength; } private: void shrinkIfNeeded(); QBitArray _flags; int _maxFlag; int _minFlag; bool _trailingFlipped; /// are the trailing properties flipping in their state (e.g. assumed true, instead of false) int _encodedLength; }; template PropertyFlags& operator<<(PropertyFlags& out, const PropertyFlags& other) { return out <<= other; } template PropertyFlags& operator<<(PropertyFlags& out, Enum flag) { return out <<= flag; } template inline void PropertyFlags::setHasProperty(Enum flag, bool value) { // keep track of our min flag if (flag < _minFlag) { if (value) { _minFlag = flag; } } if (flag > _maxFlag) { if (value) { _maxFlag = flag; _flags.resize(_maxFlag + 1); } else { return; // bail early, we're setting a flag outside of our current _maxFlag to false, which is already the default } } _flags.setBit(flag, value); if (flag == _maxFlag && !value) { shrinkIfNeeded(); } } template inline bool PropertyFlags::getHasProperty(Enum flag) const { if (flag > _maxFlag) { return _trailingFlipped; // usually false } return _flags.testBit(flag); } const int BITS_PER_BYTE = 8; template inline QByteArray PropertyFlags::encode() { QByteArray output; if (_maxFlag < _minFlag) { output.fill(0, 1); return output; // no flags... nothing to encode } // we should size the array to the correct size. int lengthInBytes = (_maxFlag / (BITS_PER_BYTE - 1)) + 1; output.fill(0, lengthInBytes); // 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 < lengthInBytes; i++) { int outputIndex = i; int bitValue = (i < (lengthInBytes - 1) ? 1 : 0); char original = output.at(outputIndex / BITS_PER_BYTE); int shiftBy = BITS_PER_BYTE - ((outputIndex % BITS_PER_BYTE) + 1); char thisBit = ( bitValue << shiftBy); output[i / BITS_PER_BYTE] = (original | thisBit); } // finally pack the the actual bits from the bit array for(int i = lengthInBytes; i < (lengthInBytes + _maxFlag + 1); i++) { int flagIndex = i - lengthInBytes; int outputIndex = i; int bitValue = ( _flags[flagIndex] ? 1 : 0); char original = output.at(outputIndex / BITS_PER_BYTE); int shiftBy = BITS_PER_BYTE - ((outputIndex % BITS_PER_BYTE) + 1); char thisBit = ( bitValue << shiftBy); output[i / BITS_PER_BYTE] = (original | thisBit); } _encodedLength = lengthInBytes; return output; } template inline size_t PropertyFlags::decode(const uint8_t* data, size_t size) { clear(); // we are cleared out! size_t bytesConsumed = 0; int bitCount = BITS_IN_BYTE * (int)size; 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 = 0; for (unsigned 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(bitAt - leadBits), true); } } bitAt++; maskBit >>= 1; } if (!inLeadBits && bitAt > lastValueBit) { break; } } _encodedLength = (int)bytesConsumed; return bytesConsumed; } template inline size_t PropertyFlags::decode(const QByteArray& fromEncodedBytes) { return decode(reinterpret_cast(fromEncodedBytes.data()), fromEncodedBytes.size()); } template inline void PropertyFlags::debugDumpBits() { qCDebug(shared) << "_minFlag=" << _minFlag; qCDebug(shared) << "_maxFlag=" << _maxFlag; qCDebug(shared) << "_trailingFlipped=" << _trailingFlipped; QString bits; for(int i = 0; i < _flags.size(); i++) { bits += (_flags.at(i) ? "1" : "0"); } qCDebug(shared) << "bits:" << bits; } template inline PropertyFlags& PropertyFlags::operator=(const PropertyFlags& other) { _flags = other._flags; _maxFlag = other._maxFlag; _minFlag = other._minFlag; return *this; } template inline PropertyFlags& PropertyFlags::operator|=(const PropertyFlags& other) { _flags |= other._flags; _maxFlag = std::max(_maxFlag, other._maxFlag); _minFlag = std::min(_minFlag, other._minFlag); return *this; } template inline PropertyFlags& PropertyFlags::operator|=(Enum flag) { PropertyFlags other(flag); _flags |= other._flags; _maxFlag = std::max(_maxFlag, other._maxFlag); _minFlag = std::min(_minFlag, other._minFlag); return *this; } template inline PropertyFlags& PropertyFlags::operator&=(const PropertyFlags& other) { _flags &= other._flags; shrinkIfNeeded(); return *this; } template inline PropertyFlags& PropertyFlags::operator&=(Enum flag) { PropertyFlags other(flag); _flags &= other._flags; shrinkIfNeeded(); return *this; } template inline PropertyFlags& PropertyFlags::operator^=(const PropertyFlags& other) { _flags ^= other._flags; shrinkIfNeeded(); return *this; } template inline PropertyFlags& PropertyFlags::operator^=(Enum flag) { PropertyFlags other(flag); _flags ^= other._flags; shrinkIfNeeded(); return *this; } template inline PropertyFlags& PropertyFlags::operator+=(const PropertyFlags& other) { for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) { if (other.getHasProperty((Enum)flag)) { setHasProperty((Enum)flag, true); } } return *this; } template inline PropertyFlags& PropertyFlags::operator+=(Enum flag) { setHasProperty(flag, true); return *this; } template inline PropertyFlags& PropertyFlags::operator-=(const PropertyFlags& other) { for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) { if (other.getHasProperty((Enum)flag)) { setHasProperty((Enum)flag, false); } } return *this; } template inline PropertyFlags& PropertyFlags::operator-=(Enum flag) { setHasProperty(flag, false); return *this; } template inline PropertyFlags& PropertyFlags::operator<<=(const PropertyFlags& other) { for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) { if (other.getHasProperty((Enum)flag)) { setHasProperty((Enum)flag, true); } } return *this; } template inline PropertyFlags& PropertyFlags::operator<<=(Enum flag) { setHasProperty(flag, true); return *this; } template inline PropertyFlags PropertyFlags::operator|(const PropertyFlags& other) const { PropertyFlags result(*this); result |= other; return result; } template inline PropertyFlags PropertyFlags::operator|(Enum flag) const { PropertyFlags result(*this); PropertyFlags other(flag); result |= other; return result; } template inline PropertyFlags PropertyFlags::operator&(const PropertyFlags& other) const { PropertyFlags result(*this); result &= other; return result; } template inline PropertyFlags PropertyFlags::operator&(Enum flag) const { PropertyFlags result(*this); PropertyFlags other(flag); result &= other; return result; } template inline PropertyFlags PropertyFlags::operator^(const PropertyFlags& other) const { PropertyFlags result(*this); result ^= other; return result; } template inline PropertyFlags PropertyFlags::operator^(Enum flag) const { PropertyFlags result(*this); PropertyFlags other(flag); result ^= other; return result; } template inline PropertyFlags PropertyFlags::operator+(const PropertyFlags& other) const { PropertyFlags result(*this); result += other; return result; } template inline PropertyFlags PropertyFlags::operator+(Enum flag) const { PropertyFlags result(*this); result.setHasProperty(flag, true); return result; } template inline PropertyFlags PropertyFlags::operator-(const PropertyFlags& other) const { PropertyFlags result(*this); result -= other; return result; } template inline PropertyFlags PropertyFlags::operator-(Enum flag) const { PropertyFlags result(*this); result.setHasProperty(flag, false); return result; } template inline PropertyFlags PropertyFlags::operator<<(const PropertyFlags& other) const { PropertyFlags result(*this); result <<= other; return result; } template inline PropertyFlags PropertyFlags::operator<<(Enum flag) const { PropertyFlags result(*this); result.setHasProperty(flag, true); return result; } template inline PropertyFlags PropertyFlags::operator~() const { PropertyFlags result(*this); result._flags = ~_flags; result._trailingFlipped = !_trailingFlipped; return result; } template inline void PropertyFlags::shrinkIfNeeded() { int maxFlagWas = _maxFlag; while (_maxFlag >= 0) { if (_flags.testBit(_maxFlag)) { break; } _maxFlag--; } if (maxFlagWas != _maxFlag) { _flags.resize(_maxFlag + 1); } } template inline QByteArray& operator<<(QByteArray& out, PropertyFlags& value) { return out = value; } template inline QByteArray& operator>>(QByteArray& in, PropertyFlags& value) { value.decode(in); return in; } #endif // hifi_PropertyFlags_h