Overte C++ Documentation
ByteCountCoding.h
1 //
2 // ByteCountCoding.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 // * handle signed types better, check for - numeric_limits<type>::is_signed
14 // * test extra long buffer with garbage data at end...
15 
16 #ifndef hifi_ByteCountCoding_h
17 #define hifi_ByteCountCoding_h
18 
19 #include <algorithm>
20 #include <cassert>
21 #include <climits>
22 #include <limits>
23 
24 #include <QDebug>
25 
26 #include <QBitArray>
27 #include <QByteArray>
28 
29 #include "SharedUtil.h"
30 
31 #include "NumericalConstants.h"
32 
33 template<typename T> class ByteCountCoded {
34 public:
35  T data;
36  ByteCountCoded(T input = 0) : data(input) {
37  // only use this template for non-signed integer types
38  assert(!std::numeric_limits<T>::is_signed && std::numeric_limits<T>::is_integer);
39  };
40 
41  ByteCountCoded(const QByteArray& fromEncoded) : data(0) { decode(fromEncoded); }
42 
43  QByteArray encode() const;
44  size_t decode(const QByteArray& fromEncoded);
45  size_t decode(const char* encodedBuffer, int encodedSize);
46 
47  bool operator==(const ByteCountCoded& other) const { return data == other.data; }
48  bool operator!=(const ByteCountCoded& other) const { return data != other.data; }
49  bool operator!() const { return data == 0; }
50 
51  operator QByteArray() const { return encode(); };
52  operator T() const { return data; };
53 };
54 
55 template<typename T> inline QByteArray& operator<<(QByteArray& out, const ByteCountCoded<T>& value) {
56  return out = value;
57 }
58 
59 template<typename T> inline QByteArray& operator>>(QByteArray& in, ByteCountCoded<T>& value) {
60  value.decode(in);
61  return in;
62 }
63 
64 template<typename T> inline QByteArray ByteCountCoded<T>::encode() const {
65  QByteArray output;
66 
67  int totalBits = sizeof(data) * BITS_IN_BYTE;
68  int valueBits = totalBits;
69  bool firstValueFound = false;
70  T temp = data;
71  T lastBitMask = (T)(1) << (totalBits - 1);
72 
73  // determine the number of bits that the value takes
74  for (int bitAt = 0; bitAt < totalBits; bitAt++) {
75  T bitValue = (temp & lastBitMask) == lastBitMask;
76  if (!firstValueFound) {
77  if (bitValue == 0) {
78  valueBits--;
79  } else {
80  firstValueFound = true;
81  }
82  }
83  temp = temp << 1;
84  }
85 
86  // calculate the number of total bytes, including our header
87  // BITS_IN_BYTE-1 because we need to code the number of bytes in the header
88  // + 1 because we always take at least 1 byte, even if number of bits is less than a bytes worth
89  int numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1;
90 
91  output.fill(0, numberOfBytes);
92 
93  // next pack the number of header bits in, the first N-1 to be set to 1, the last to be set to 0
94  for(int i = 0; i < numberOfBytes; i++) {
95  int outputIndex = i;
96  T bitValue = (i < (numberOfBytes - 1) ? 1 : 0);
97  char original = output.at(outputIndex / BITS_IN_BYTE);
98  int shiftBy = BITS_IN_BYTE - ((outputIndex % BITS_IN_BYTE) + 1);
99  char thisBit = ( bitValue << shiftBy);
100  output[i / BITS_IN_BYTE] = (original | thisBit);
101  }
102 
103  // finally pack the the actual bits from the bit array
104  temp = data;
105  for(int i = numberOfBytes; i < (numberOfBytes + valueBits); i++) {
106  int outputIndex = i;
107  T bitValue = (temp & 1);
108  char original = output.at(outputIndex / BITS_IN_BYTE);
109  int shiftBy = BITS_IN_BYTE - ((outputIndex % BITS_IN_BYTE) + 1);
110  char thisBit = ( bitValue << shiftBy);
111  output[i / BITS_IN_BYTE] = (original | thisBit);
112 
113  temp = temp >> 1;
114  }
115  return output;
116 }
117 
118 template<typename T> inline size_t ByteCountCoded<T>::decode(const QByteArray& fromEncodedBytes) {
119  return decode(fromEncodedBytes.constData(), fromEncodedBytes.size());
120 }
121 
122 template<typename T> inline size_t ByteCountCoded<T>::decode(const char* encodedBuffer, int encodedSize) {
123  data = 0; // reset data
124  size_t bytesConsumed = 0;
125  int bitCount = BITS_IN_BYTE * encodedSize;
126 
127  int encodedByteCount = 1; // there is at least 1 byte (after the leadBits)
128  int leadBits = 1; // there is always at least 1 lead bit
129  bool inLeadBits = true;
130  int bitAt = 0;
131  int expectedBitCount; // unknown at this point
132  int lastValueBit = 0;
133  T bitValue = 1;
134 
135  for(int byte = 0; byte < encodedSize; byte++) {
136  char originalByte = encodedBuffer[byte];
137  bytesConsumed++;
138  unsigned char maskBit = 128; // LEFT MOST BIT set
139  for(int bit = 0; bit < BITS_IN_BYTE; bit++) {
140  bool bitIsSet = originalByte & maskBit;
141 
142  // Processing of the lead bits
143  if (inLeadBits) {
144  if (bitIsSet) {
145  encodedByteCount++;
146  leadBits++;
147  } else {
148  inLeadBits = false; // once we hit our first 0, we know we're out of the lead bits
149  expectedBitCount = (encodedByteCount * BITS_IN_BYTE) - leadBits;
150  lastValueBit = expectedBitCount + bitAt;
151 
152  // check to see if the remainder of our buffer is sufficient
153  if (expectedBitCount > (bitCount - leadBits)) {
154  break;
155  }
156  }
157  } else {
158  if (bitAt > lastValueBit) {
159  break;
160  }
161 
162  if(bitIsSet) {
163  data += bitValue;
164  }
165  bitValue *= 2;
166  }
167  bitAt++;
168  maskBit = maskBit >> 1;
169  }
170  if (!inLeadBits && bitAt > lastValueBit) {
171  break;
172  }
173  }
174  return bytesConsumed;
175 }
176 #endif // hifi_ByteCountCoding_h
177