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