blob: 844a9404fa16ee1663fab51674f10061fab344f3 [file] [log] [blame]
Brian Silverman246cb222019-02-02 16:38:18 -08001#ifndef AOS_UTIL_BITPACKING_H_
2#define AOS_UTIL_BITPACKING_H_
3
Tyler Chatowbf0609c2021-07-31 16:13:27 -07004#include <cassert>
Brian Silverman246cb222019-02-02 16:38:18 -08005#include <type_traits>
6
Austin Schuhb72be802022-01-02 12:26:28 -08007#include "absl/types/span.h"
Brian Silverman246cb222019-02-02 16:38:18 -08008
9namespace aos {
10
11template <typename Integer>
12typename std::enable_if<std::is_unsigned<Integer>::value, Integer>::type
13MaskOnes(size_t bits) {
14 // Get these edge cases out of the way first, so we can subtract 1 from bits
15 // safely later without getting a negative number.
16 if (bits == 0) {
17 return 0;
18 }
19 if (bits == 1) {
20 return 1;
21 }
22 static constexpr Integer kOne = 1;
23 // Note that we shift at most by bits - 1. bits == sizeof(Integer) * 8 is
24 // valid, and shifting by the width of a type is undefined behavior, so we
25 // need to get a bit fancy to make it all work. Just ORing high_bit in
26 // explicitly at the end makes it work.
27 const Integer high_bit = kOne << (bits - 1);
28 return (high_bit - kOne) | high_bit;
29}
30
31template <typename Integer, size_t bits, size_t offset>
32typename std::enable_if<std::is_unsigned<Integer>::value &&
33 sizeof(Integer) * 8 >= bits>::type
Austin Schuhb72be802022-01-02 12:26:28 -080034PackBits(const Integer value, const absl::Span<char> destination) {
Brian Silverman246cb222019-02-02 16:38:18 -080035 assert(static_cast<size_t>(destination.size()) * 8u >= bits + offset);
36 size_t bits_completed = 0;
37 while (bits_completed < bits) {
38 // Which logical bit (through all the bytes) we're writing at.
39 const size_t output_bit = offset + bits_completed;
40 // The lowest-numbered bit in the current byte we're writing to.
41 const size_t output_min_bit = output_bit % 8;
42 // The number of bits we're writing in this byte.
43 const size_t new_bits = std::min(8 - output_min_bit, bits - bits_completed);
44 // The highest-numbered bit in the current byte we're writing to.
45 const size_t output_max_bit = output_min_bit + new_bits;
46 // The new bits to set in the this byte.
47 const uint8_t new_byte_part =
48 (value >> bits_completed) & MaskOnes<Integer>(new_bits);
49 // A mask of bits to keep from the current value of this byte. Start with
50 // just the low ones.
51 uint8_t existing_mask = MaskOnes<uint8_t>(output_min_bit);
52 // And then add in the high bits to keep.
53 existing_mask |= MaskOnes<uint8_t>(std::max<int>(8 - output_max_bit, 0))
54 << output_max_bit;
55 // The index of the byte we're writing to.
56 const size_t byte_index = output_bit / 8;
57 // The full new value of the current byte. Start with just the existing bits
58 // we're not touching.
59 uint8_t new_byte = destination[byte_index] & existing_mask;
60 // Add in the new part.
61 new_byte |= new_byte_part << output_min_bit;
62 destination[byte_index] = new_byte;
63 bits_completed += new_bits;
64 }
65 assert(bits_completed == bits);
66}
67
68template <typename Integer, size_t bits, size_t offset>
69typename std::enable_if<std::is_unsigned<Integer>::value &&
Tyler Chatowbf0609c2021-07-31 16:13:27 -070070 sizeof(Integer) * 8 >= bits,
71 Integer>::type
Austin Schuhb72be802022-01-02 12:26:28 -080072UnpackBits(const absl::Span<const char> source) {
Brian Silverman246cb222019-02-02 16:38:18 -080073 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_