Overte C++ Documentation
PropertyFlags.h
1 //
2 // PropertyFlags.h
3 // libraries/shared/src
4 //
5 // Created by Brad Hefta-Gaub on 6/3/14.
6 // Copyright 2014 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 //
12 // TODO:
13 // * consider adding iterator to enumerate the properties that have been set?
14 // * operator QSet<Enum> - this would be easiest way to handle enumeration
15 // * make encode(), QByteArray<< operator, and QByteArray operator const by moving calculation of encoded length to
16 // setFlag() and other calls
17 // * should the QByteArray<< operator and QByteArray>> operator return the shifted versions of the byte arrays?
18 
19 #ifndef hifi_PropertyFlags_h
20 #define hifi_PropertyFlags_h
21 
22 #include <algorithm>
23 #include <climits>
24 
25 #include <QBitArray>
26 #include <QByteArray>
27 
28 #include "ByteCountCoding.h"
29 #include "SharedLogging.h"
30 
31 template<typename Enum>class PropertyFlags {
32 public:
33  typedef Enum enum_type;
34  inline PropertyFlags() :
35  _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { };
36 
37  inline PropertyFlags(const PropertyFlags& other) :
38  _flags(other._flags), _maxFlag(other._maxFlag), _minFlag(other._minFlag),
39  _trailingFlipped(other._trailingFlipped), _encodedLength(0) {}
40 
41  inline PropertyFlags(Enum flag) :
42  _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { setHasProperty(flag); }
43 
44  inline PropertyFlags(const QByteArray& fromEncoded) :
45  _maxFlag(INT_MIN), _minFlag(INT_MAX), _trailingFlipped(false), _encodedLength(0) { decode(fromEncoded); }
46 
47  void clear() { _flags.clear(); _maxFlag = INT_MIN; _minFlag = INT_MAX; _trailingFlipped = false; _encodedLength = 0; }
48  bool isEmpty() const { return _maxFlag == INT_MIN && _minFlag == INT_MAX && _trailingFlipped == false && _encodedLength == 0; }
49 
50  Enum firstFlag() const { return (Enum)_minFlag; }
51  Enum lastFlag() const { return (Enum)_maxFlag; }
52 
53  void setHasProperty(Enum flag, bool value = true);
54  bool getHasProperty(Enum flag) const;
55  QByteArray encode();
56  size_t decode(const uint8_t* data, size_t length);
57  size_t decode(const QByteArray& fromEncoded);
58 
59  operator QByteArray() { return encode(); };
60 
61  bool operator==(const PropertyFlags& other) const { return _flags == other._flags; }
62  bool operator!=(const PropertyFlags& other) const { return _flags != other._flags; }
63  bool operator!() const { return _flags.size() == 0; }
64 
65  PropertyFlags& operator=(const PropertyFlags& other);
66 
67  PropertyFlags& operator|=(const PropertyFlags& other);
68  PropertyFlags& operator|=(Enum flag);
69 
70  PropertyFlags& operator&=(const PropertyFlags& other);
71  PropertyFlags& operator&=(Enum flag);
72 
73  PropertyFlags& operator+=(const PropertyFlags& other);
74  PropertyFlags& operator+=(Enum flag);
75 
76  PropertyFlags& operator-=(const PropertyFlags& other);
77  PropertyFlags& operator-=(Enum flag);
78 
79  PropertyFlags& operator<<=(const PropertyFlags& other);
80  PropertyFlags& operator<<=(Enum flag);
81 
82  PropertyFlags operator|(const PropertyFlags& other) const;
83  PropertyFlags operator|(Enum flag) const;
84 
85  PropertyFlags operator&(const PropertyFlags& other) const;
86  PropertyFlags operator&(Enum flag) const;
87 
88  PropertyFlags operator+(const PropertyFlags& other) const;
89  PropertyFlags operator+(Enum flag) const;
90 
91  PropertyFlags operator-(const PropertyFlags& other) const;
92  PropertyFlags operator-(Enum flag) const;
93 
94  PropertyFlags operator<<(const PropertyFlags& other) const;
95  PropertyFlags operator<<(Enum flag) const;
96 
97  // NOTE: due to the nature of the compact storage of these property flags, and the fact that the upper bound of the
98  // enum is not known, these operators will only perform their bitwise operations on the set of properties that have
99  // been previously set
100  PropertyFlags& operator^=(const PropertyFlags& other);
101  PropertyFlags& operator^=(Enum flag);
102  PropertyFlags operator^(const PropertyFlags& other) const;
103  PropertyFlags operator^(Enum flag) const;
104  PropertyFlags operator~() const;
105 
106  void debugDumpBits();
107 
108  int getEncodedLength() const { return _encodedLength; }
109 
110 
111 private:
112  void shrinkIfNeeded();
113 
114  QBitArray _flags;
115  int _maxFlag;
116  int _minFlag;
117  bool _trailingFlipped;
118  int _encodedLength;
119 };
120 
121 template<typename Enum> PropertyFlags<Enum>& operator<<(PropertyFlags<Enum>& out, const PropertyFlags<Enum>& other) {
122  return out <<= other;
123 }
124 
125 template<typename Enum> PropertyFlags<Enum>& operator<<(PropertyFlags<Enum>& out, Enum flag) {
126  return out <<= flag;
127 }
128 
129 
130 template<typename Enum> inline void PropertyFlags<Enum>::setHasProperty(Enum flag, bool value) {
131  // keep track of our min flag
132  if (flag < _minFlag) {
133  if (value) {
134  _minFlag = flag;
135  }
136  }
137  if (flag > _maxFlag) {
138  if (value) {
139  _maxFlag = flag;
140  _flags.resize(_maxFlag + 1);
141  } else {
142  return; // bail early, we're setting a flag outside of our current _maxFlag to false, which is already the default
143  }
144  }
145  _flags.setBit(flag, value);
146 
147  if (flag == _maxFlag && !value) {
148  shrinkIfNeeded();
149  }
150 }
151 
152 template<typename Enum> inline bool PropertyFlags<Enum>::getHasProperty(Enum flag) const {
153  if (flag > _maxFlag) {
154  return _trailingFlipped; // usually false
155  }
156  return _flags.testBit(flag);
157 }
158 
159 const int BITS_PER_BYTE = 8;
160 
161 template<typename Enum> inline QByteArray PropertyFlags<Enum>::encode() {
162  QByteArray output;
163 
164  if (_maxFlag < _minFlag) {
165  output.fill(0, 1);
166  return output; // no flags... nothing to encode
167  }
168 
169  // we should size the array to the correct size.
170  int lengthInBytes = (_maxFlag / (BITS_PER_BYTE - 1)) + 1;
171 
172  output.fill(0, lengthInBytes);
173 
174  // next pack the number of header bits in, the first N-1 to be set to 1, the last to be set to 0
175  for(int i = 0; i < lengthInBytes; i++) {
176  int outputIndex = i;
177  int bitValue = (i < (lengthInBytes - 1) ? 1 : 0);
178  char original = output.at(outputIndex / BITS_PER_BYTE);
179  int shiftBy = BITS_PER_BYTE - ((outputIndex % BITS_PER_BYTE) + 1);
180  char thisBit = ( bitValue << shiftBy);
181  output[i / BITS_PER_BYTE] = (original | thisBit);
182  }
183 
184  // finally pack the the actual bits from the bit array
185  for(int i = lengthInBytes; i < (lengthInBytes + _maxFlag + 1); i++) {
186  int flagIndex = i - lengthInBytes;
187  int outputIndex = i;
188  int bitValue = ( _flags[flagIndex] ? 1 : 0);
189  char original = output.at(outputIndex / BITS_PER_BYTE);
190  int shiftBy = BITS_PER_BYTE - ((outputIndex % BITS_PER_BYTE) + 1);
191  char thisBit = ( bitValue << shiftBy);
192  output[i / BITS_PER_BYTE] = (original | thisBit);
193  }
194 
195  _encodedLength = lengthInBytes;
196  return output;
197 }
198 
199 template<typename Enum>
200 inline size_t PropertyFlags<Enum>::decode(const uint8_t* data, size_t size) {
201  clear(); // we are cleared out!
202 
203  size_t bytesConsumed = 0;
204  int bitCount = BITS_IN_BYTE * (int)size;
205 
206  int encodedByteCount = 1; // there is at least 1 byte (after the leadBits)
207  int leadBits = 1; // there is always at least 1 lead bit
208  bool inLeadBits = true;
209  int bitAt = 0;
210  int expectedBitCount; // unknown at this point
211  int lastValueBit = 0;
212  for (unsigned int byte = 0; byte < size; byte++) {
213  char originalByte = data[byte];
214  bytesConsumed++;
215  unsigned char maskBit = 0x80; // LEFT MOST BIT set
216  for (int bit = 0; bit < BITS_IN_BYTE; bit++) {
217  bool bitIsSet = originalByte & maskBit;
218  // Processing of the lead bits
219  if (inLeadBits) {
220  if (bitIsSet) {
221  encodedByteCount++;
222  leadBits++;
223  } else {
224  inLeadBits = false; // once we hit our first 0, we know we're out of the lead bits
225  expectedBitCount = (encodedByteCount * BITS_IN_BYTE) - leadBits;
226  lastValueBit = expectedBitCount + bitAt;
227 
228  // check to see if the remainder of our buffer is sufficient
229  if (expectedBitCount > (bitCount - leadBits)) {
230  break;
231  }
232  }
233  } else {
234  if (bitAt > lastValueBit) {
235  break;
236  }
237 
238  if (bitIsSet) {
239  setHasProperty(static_cast<Enum>(bitAt - leadBits), true);
240  }
241  }
242  bitAt++;
243  maskBit >>= 1;
244  }
245  if (!inLeadBits && bitAt > lastValueBit) {
246  break;
247  }
248  }
249  _encodedLength = (int)bytesConsumed;
250  return bytesConsumed;
251 }
252 
253 template<typename Enum> inline size_t PropertyFlags<Enum>::decode(const QByteArray& fromEncodedBytes) {
254  return decode(reinterpret_cast<const uint8_t*>(fromEncodedBytes.data()), fromEncodedBytes.size());
255 }
256 
257 template<typename Enum> inline void PropertyFlags<Enum>::debugDumpBits() {
258  qCDebug(shared) << "_minFlag=" << _minFlag;
259  qCDebug(shared) << "_maxFlag=" << _maxFlag;
260  qCDebug(shared) << "_trailingFlipped=" << _trailingFlipped;
261  QString bits;
262  for(int i = 0; i < _flags.size(); i++) {
263  bits += (_flags.at(i) ? "1" : "0");
264  }
265  qCDebug(shared) << "bits:" << bits;
266 }
267 
268 
269 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator=(const PropertyFlags& other) {
270  _flags = other._flags;
271  _maxFlag = other._maxFlag;
272  _minFlag = other._minFlag;
273  return *this;
274 }
275 
276 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator|=(const PropertyFlags& other) {
277  _flags |= other._flags;
278  _maxFlag = std::max(_maxFlag, other._maxFlag);
279  _minFlag = std::min(_minFlag, other._minFlag);
280  return *this;
281 }
282 
283 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator|=(Enum flag) {
284  PropertyFlags other(flag);
285  _flags |= other._flags;
286  _maxFlag = std::max(_maxFlag, other._maxFlag);
287  _minFlag = std::min(_minFlag, other._minFlag);
288  return *this;
289 }
290 
291 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator&=(const PropertyFlags& other) {
292  _flags &= other._flags;
293  shrinkIfNeeded();
294  return *this;
295 }
296 
297 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator&=(Enum flag) {
298  PropertyFlags other(flag);
299  _flags &= other._flags;
300  shrinkIfNeeded();
301  return *this;
302 }
303 
304 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator^=(const PropertyFlags& other) {
305  _flags ^= other._flags;
306  shrinkIfNeeded();
307  return *this;
308 }
309 
310 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator^=(Enum flag) {
311  PropertyFlags other(flag);
312  _flags ^= other._flags;
313  shrinkIfNeeded();
314  return *this;
315 }
316 
317 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator+=(const PropertyFlags& other) {
318  for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) {
319  if (other.getHasProperty((Enum)flag)) {
320  setHasProperty((Enum)flag, true);
321  }
322  }
323  return *this;
324 }
325 
326 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator+=(Enum flag) {
327  setHasProperty(flag, true);
328  return *this;
329 }
330 
331 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator-=(const PropertyFlags& other) {
332  for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) {
333  if (other.getHasProperty((Enum)flag)) {
334  setHasProperty((Enum)flag, false);
335  }
336  }
337  return *this;
338 }
339 
340 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator-=(Enum flag) {
341  setHasProperty(flag, false);
342  return *this;
343 }
344 
345 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator<<=(const PropertyFlags& other) {
346  for(int flag = (int)other.firstFlag(); flag <= (int)other.lastFlag(); flag++) {
347  if (other.getHasProperty((Enum)flag)) {
348  setHasProperty((Enum)flag, true);
349  }
350  }
351  return *this;
352 }
353 
354 template<typename Enum> inline PropertyFlags<Enum>& PropertyFlags<Enum>::operator<<=(Enum flag) {
355  setHasProperty(flag, true);
356  return *this;
357 }
358 
359 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator|(const PropertyFlags& other) const {
360  PropertyFlags result(*this);
361  result |= other;
362  return result;
363 }
364 
365 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator|(Enum flag) const {
366  PropertyFlags result(*this);
367  PropertyFlags other(flag);
368  result |= other;
369  return result;
370 }
371 
372 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator&(const PropertyFlags& other) const {
373  PropertyFlags result(*this);
374  result &= other;
375  return result;
376 }
377 
378 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator&(Enum flag) const {
379  PropertyFlags result(*this);
380  PropertyFlags other(flag);
381  result &= other;
382  return result;
383 }
384 
385 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator^(const PropertyFlags& other) const {
386  PropertyFlags result(*this);
387  result ^= other;
388  return result;
389 }
390 
391 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator^(Enum flag) const {
392  PropertyFlags result(*this);
393  PropertyFlags other(flag);
394  result ^= other;
395  return result;
396 }
397 
398 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator+(const PropertyFlags& other) const {
399  PropertyFlags result(*this);
400  result += other;
401  return result;
402 }
403 
404 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator+(Enum flag) const {
405  PropertyFlags result(*this);
406  result.setHasProperty(flag, true);
407  return result;
408 }
409 
410 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator-(const PropertyFlags& other) const {
411  PropertyFlags result(*this);
412  result -= other;
413  return result;
414 }
415 
416 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator-(Enum flag) const {
417  PropertyFlags result(*this);
418  result.setHasProperty(flag, false);
419  return result;
420 }
421 
422 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator<<(const PropertyFlags& other) const {
423  PropertyFlags result(*this);
424  result <<= other;
425  return result;
426 }
427 
428 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator<<(Enum flag) const {
429  PropertyFlags result(*this);
430  result.setHasProperty(flag, true);
431  return result;
432 }
433 
434 template<typename Enum> inline PropertyFlags<Enum> PropertyFlags<Enum>::operator~() const {
435  PropertyFlags result(*this);
436  result._flags = ~_flags;
437  result._trailingFlipped = !_trailingFlipped;
438  return result;
439 }
440 
441 template<typename Enum> inline void PropertyFlags<Enum>::shrinkIfNeeded() {
442  int maxFlagWas = _maxFlag;
443  while (_maxFlag >= 0) {
444  if (_flags.testBit(_maxFlag)) {
445  break;
446  }
447  _maxFlag--;
448  }
449  if (maxFlagWas != _maxFlag) {
450  _flags.resize(_maxFlag + 1);
451  }
452 }
453 
454 template<typename Enum> inline QByteArray& operator<<(QByteArray& out, PropertyFlags<Enum>& value) {
455  return out = value;
456 }
457 
458 template<typename Enum> inline QByteArray& operator>>(QByteArray& in, PropertyFlags<Enum>& value) {
459  value.decode(in);
460  return in;
461 }
462 
463 #endif // hifi_PropertyFlags_h
464