blob: df6ad16506f1d4c85f59f2080fcdb1123ea2be43 [file] [log] [blame]
Brian Silverman246cb222019-02-02 16:38:18 -08001#ifndef AOS_UTIL_BITPACKING_H_
2#define AOS_UTIL_BITPACKING_H_
3
4#include <assert.h>
5
6#include <type_traits>
7
8#include "third_party/GSL/include/gsl/gsl"
9
10namespace aos {
11
12template <typename Integer>
13typename std::enable_if<std::is_unsigned<Integer>::value, Integer>::type
14MaskOnes(size_t bits) {
15 // Get these edge cases out of the way first, so we can subtract 1 from bits
16 // safely later without getting a negative number.
17 if (bits == 0) {
18 return 0;
19 }
20 if (bits == 1) {
21 return 1;
22 }
23 static constexpr Integer kOne = 1;
24 // Note that we shift at most by bits - 1. bits == sizeof(Integer) * 8 is
25 // valid, and shifting by the width of a type is undefined behavior, so we
26 // need to get a bit fancy to make it all work. Just ORing high_bit in
27 // explicitly at the end makes it work.
28 const Integer high_bit = kOne << (bits - 1);
29 return (high_bit - kOne) | high_bit;
30}
31
32template <typename Integer, size_t bits, size_t offset>
33typename std::enable_if<std::is_unsigned<Integer>::value &&
34 sizeof(Integer) * 8 >= bits>::type
35PackBits(const Integer value, const gsl::span<char> destination) {
36 assert(static_cast<size_t>(destination.size()) * 8u >= bits + offset);
37 size_t bits_completed = 0;
38 while (bits_completed < bits) {
39 // Which logical bit (through all the bytes) we're writing at.
40 const size_t output_bit = offset + bits_completed;
41 // The lowest-numbered bit in the current byte we're writing to.
42 const size_t output_min_bit = output_bit % 8;
43 // The number of bits we're writing in this byte.
44 const size_t new_bits = std::min(8 - output_min_bit, bits - bits_completed);
45 // The highest-numbered bit in the current byte we're writing to.
46 const size_t output_max_bit = output_min_bit + new_bits;
47 // The new bits to set in the this byte.
48 const uint8_t new_byte_part =
49 (value >> bits_completed) & MaskOnes<Integer>(new_bits);
50 // A mask of bits to keep from the current value of this byte. Start with
51 // just the low ones.
52 uint8_t existing_mask = MaskOnes<uint8_t>(output_min_bit);
53 // And then add in the high bits to keep.
54 existing_mask |= MaskOnes<uint8_t>(std::max<int>(8 - output_max_bit, 0))
55 << output_max_bit;
56 // The index of the byte we're writing to.
57 const size_t byte_index = output_bit / 8;
58 // The full new value of the current byte. Start with just the existing bits
59 // we're not touching.
60 uint8_t new_byte = destination[byte_index] & existing_mask;
61 // Add in the new part.
62 new_byte |= new_byte_part << output_min_bit;
63 destination[byte_index] = new_byte;
64 bits_completed += new_bits;
65 }
66 assert(bits_completed == bits);
67}
68
69template <typename Integer, size_t bits, size_t offset>
70typename std::enable_if<std::is_unsigned<Integer>::value &&
71 sizeof(Integer) * 8 >= bits, Integer>::type
72UnpackBits(const gsl::span<const char> source) {
73 Integer result = 0;
74 assert(static_cast<size_t>(source.size()) * 8u >= bits + offset);
75 size_t bits_completed = 0;
76 while (bits_completed < bits) {
77 // Which logical bit (through all the bytes) we're reading at.
78 const size_t input_bit = offset + bits_completed;
79 // The lowest-numbered bit in the current byte we're reading from.
80 const size_t input_min_bit = input_bit % 8;
81 // The number of bits we're reading in this byte.
82 const size_t new_bits = std::min(8 - input_min_bit, bits - bits_completed);
83 // The index of the byte we're reading from.
84 const size_t byte_index = input_bit / 8;
85 // The part of the current byte we're actually reading.
86 const uint8_t new_byte_part =
87 (source[byte_index] >> input_min_bit) & MaskOnes<Integer>(new_bits);
88 result |= static_cast<Integer>(new_byte_part) << bits_completed;
89 bits_completed += new_bits;
90 }
91 assert(bits_completed == bits);
92 return result;
93}
94
95template <int bits>
96uint32_t FloatToIntLinear(float min, float max, float value) {
97 static_assert(bits <= 31, "Only support 32-bit outputs for now");
98 static_assert(bits >= 1, "Bits must be positive");
99 // Start such that value in [0, 1) maps to [0, 2**bits) in the final
100 // result.
101 float result = (value - min) / (max - min);
102 // Multiply so that value is in [0, 2**bits).
103 // Make sure we do the shifting in a 32-bit integer, despite C++'s weird
104 // integer promotions, which is safe because bits is at most 31.
105 result *= static_cast<uint32_t>(UINT32_C(1) << bits);
106 if (result <= 0.0f) {
107 return 0;
108 }
109 const float max_result = MaskOnes<uint32_t>(bits);
110 if (result >= max_result) {
111 return max_result;
112 }
113 return static_cast<uint32_t>(result);
114}
115
116template <int bits>
117float IntToFloatLinear(float min, float max, uint32_t value) {
118 static_assert(bits <= 31, "Only support 32-bit outputs for now");
119 static_assert(bits >= 1, "Bits must be positive");
120 const float max_value = MaskOnes<uint32_t>(bits);
121 if (value > max_value) {
122 value = max_value;
123 }
124 // Start such that result in [0, 2**bits) maps to [min, max) in the final
125 // result.
126 float result = value;
127 // Offset by half a bit so we return a value in the middle of each one.
128 // This causes us to return the middle floating point value which could be
129 // represented by a given integer value.
130 result += 0.5f;
131 // Multiply so that result is in [0, 1).
132 // Make sure we do the shifting in a 32-bit integer, despite C++'s weird
133 // integer promotions, which is safe because bits is at most 31.
134 result *= 1.0f / static_cast<uint32_t>(UINT32_C(1) << bits);
135 return min + result * (max - min);
136}
137
138} // namespace aos
139
140#endif // AOS_UTIL_BITPACKING_H_