• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 #ifndef COMMON_UTILS_NUMERIC_UTILS_H_
13 #define COMMON_UTILS_NUMERIC_UTILS_H_
14 
15 #include <algorithm>
16 #include <cmath>
17 #include <cstdint>
18 #include <limits>
19 #include <string>
20 #include <type_traits>
21 
22 #include "absl/log/check.h"
23 #include "absl/log/log.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/strings/string_view.h"
27 #include "absl/types/span.h"
28 #include "iamf/common/utils/validation_utils.h"
29 
30 namespace iamf_tools {
31 
32 /*!\brief Sums the input values and checks for overflow.
33  *
34  * \param x_1 First summand.
35  * \param x_2 Second summand.
36  * \param result Sum of the inputs on success.
37  * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` when
38  *         the sum would cause an overflow in a `uint32_t`.
39  */
40 absl::Status AddUint32CheckOverflow(uint32_t x_1, uint32_t x_2,
41                                     uint32_t& result);
42 
43 /*!\brief Converts float input to Q7.8 format.
44  *
45  * \param value Value to convert.
46  * \param result Converted value if successful. The result is floored to the
47  *        nearest Q7.8 value.
48  * \return `absl::OkStatus()` if successful. `absl::UnknownError()` if the input
49  *         is not valid in Q7.8 format.
50  */
51 absl::Status FloatToQ7_8(float value, int16_t& result);
52 
53 /*!\brief Converts Q7.8 input to float output.
54  *
55  * \param value Value to convert.
56  * \return Converted value.
57  */
58 float Q7_8ToFloat(int16_t value);
59 
60 // TODO(b/283281856): Consider removing `FloatToQ0_8()` if it is still an unused
61 //                    function after the encoder supports resampling parameter
62 //                    blocks.
63 /*!\brief Converts float input to Q0.8 format.
64  *
65  * \param value Value to convert.
66  * \param result Converted value if successful. The result is floored to the
67  *        nearest Q0.8 value.
68  * \return `absl::OkStatus()` if successful. `absl::UnknownError()` if the input
69  *         is not valid in Q0.8 format.
70  */
71 absl::Status FloatToQ0_8(float value, uint8_t& result);
72 
73 /*!\brief Converts Q0.8 input to float output.
74  *
75  * \param value Value to convert.
76  * \return Converted value.
77  */
78 float Q0_8ToFloat(uint8_t value);
79 
80 /*!\brief Typecasts the input value and writes to the output argument if valid.
81  *
82  * The custom `field_name` is used to create a more descriptive error message.
83  * This is inserted surrounded by backticks. When this refers to a user facing
84  * field (i.e. related to `UserMetadata` protos) this should refer to the
85  * user-facing field name.
86  *
87  * \param field_name Field name to insert into the error message.
88  * \param input Value to convert.
89  * \param output Converted value if successful.
90  * \return `absl::OkStatus()` if successful. `absl::InvalidArgumentError()` if
91  *         the input is outside the expected range.
92  */
93 template <typename T, typename U>
StaticCastIfInRange(absl::string_view field_name,T input,U & output)94 absl::Status StaticCastIfInRange(absl::string_view field_name, T input,
95                                  U& output) {
96   constexpr U kMinOutput = std::numeric_limits<U>::min();
97   constexpr U kMaxOutput = std::numeric_limits<U>::max();
98   if (input < kMinOutput || kMaxOutput < input) [[unlikely]] {
99     std::string message =
100         absl::StrCat(field_name, " is outside the expected range of ");
101     if constexpr (std::is_same_v<U, char> || std::is_same_v<U, unsigned char>) {
102       absl::StrAppend(&message, "[0, 255]");
103     } else {
104       absl::StrAppend(&message, "[", kMinOutput, ", ", kMaxOutput, "]");
105     }
106     return absl::InvalidArgumentError(message);
107   }
108   output = static_cast<U>(input);
109   return absl::OkStatus();
110 }
111 
112 /*!\brief Creates a 32-bit signed integer from the [1, 4] input `bytes`.
113  *
114  * \param bytes Bytes to convert.
115  * \param output Converted value if successful. The result is left-justified;
116  *        the upper `bytes.size()` bytes are set based on the input and the
117  *        remaining lower bytes are 0.
118  * \return `absl::OkStatus()` if successful. `absl::InvalidArgumentError()` if
119  *         the number of bytes is not in the range of [1, 4].
120  */
121 absl::Status LittleEndianBytesToInt32(absl::Span<const uint8_t> bytes,
122                                       int32_t& output);
123 
124 /*!\brief Creates a 32-bit signed integer from the [1, 4] input `bytes`.
125  *
126  * \param bytes Bytes to convert.
127  * \param output Converted value if successful. The result is left-justified;
128  *        the upper `bytes.size()` bytes are set based on the input and the
129  *        remaining lower bytes are 0.
130  * \return `absl::OkStatus()` if successful. `absl::InvalidArgumentError()` if
131  *         the number of bytes is not in the range of [1, 4].
132  */
133 absl::Status BigEndianBytesToInt32(absl::Span<const uint8_t> bytes,
134                                    int32_t& output);
135 
136 /*!\brief Clips and typecasts the input value and writes to the output argument.
137  *
138  * \param input Value to convert.
139  * \param output Converted value if successful.
140  * \return `absl::OkStatus()` if successful. `absl::InvalidArgumentError()` if
141  *         the input is NaN.
142  */
143 absl::Status ClipDoubleToInt32(double input, int32_t& output);
144 
145 namespace obu_util_internal {
146 
147 constexpr double kMaxInt32PlusOneAsDouble =
148     static_cast<double>(std::numeric_limits<int32_t>::max()) + 1.0;
149 
150 }  // namespace obu_util_internal
151 
152 /*!\brief Normalizes the input value to a floating point in the range [-1, +1].
153  *
154  * Normalizes the input from [std::numeric_limits<int32_t>::min(),
155  * std::numeric_limits<int32_t>::max() + 1] to [-1, +1].
156  *
157  * \param value Value to normalize.
158  * \return Normalized value.
159  */
160 template <typename T>
Int32ToNormalizedFloatingPoint(int32_t value)161 constexpr T Int32ToNormalizedFloatingPoint(int32_t value) {
162   using obu_util_internal::kMaxInt32PlusOneAsDouble;
163   static_assert(std::is_floating_point_v<T>);
164 
165   // Perform calculations in double. The final cast to the output type, e.g.
166   // `float` could result in loss of precision. Note that casting `int32_t` to
167   // `double` is lossless; every `int32_t` can be exactly represented.
168   return static_cast<T>(static_cast<double>(value) / kMaxInt32PlusOneAsDouble);
169 }
170 
171 /*!\brief Converts normalized floating point input to an `int32_t`.
172  *
173  * Transforms the input from the range of [-1, +1] to the range of
174  * [std::numeric_limits<int32_t>::min(), std::numeric_limits<int32_t>::max() +
175  * 1].
176  *
177  * Input is clamped to [-1, +1] before processing. Output is clamped to the
178  * full range of an `int32_t`.
179  *
180  * \param value Normalized floating point value to convert.
181  * \param result Converted value if successful.
182  * \return `absl::OkStatus()` if successful. `absl::InvalidArgumentError()` if
183  *         the input is any type of NaN or infinity.
184  */
185 template <typename T>
NormalizedFloatingPointToInt32(T value,int32_t & result)186 absl::Status NormalizedFloatingPointToInt32(T value, int32_t& result) {
187   using obu_util_internal::kMaxInt32PlusOneAsDouble;
188   static_assert(std::is_floating_point_v<T>);
189   if (std::isnan(value) || std::isinf(value)) {
190     return absl::InvalidArgumentError("Input is NaN or infinity.");
191   }
192 
193   const double clamped_input =
194       std::clamp(static_cast<double>(value), -1.0, 1.0);
195   // Clip the result to be safe. Although only values near
196   // `std::numeric_limits<int32_t>::max() + 1` will be out of range.
197   return ClipDoubleToInt32(clamped_input * kMaxInt32PlusOneAsDouble, result);
198 }
199 
200 /*!\brief Gets the native byte order of the runtime system.
201  *
202  * \return `true` if the runtime system natively uses big endian, `false`
203  *         otherwise.
204  */
205 bool IsNativeBigEndian();
206 
207 /*!\brief Casts and copies the input span to the output span.
208  *
209  * \param field_name Field name of the vector to insert into the error message.
210  * \param vector_size Size of the vector.
211  * \param reported_size Size reported by associated fields (e.g. "*_size" fields
212  *                      in the OBU).
213  * \return `absl::OkStatus()` if the size arguments are equivalent.
214  *         `absl::InvalidArgumentError()` otherwise.
215  */
216 template <typename T, typename U>
StaticCastSpanIfInRange(absl::string_view field_name,absl::Span<const T> input_data,absl::Span<U> output_data)217 absl::Status StaticCastSpanIfInRange(absl::string_view field_name,
218                                      absl::Span<const T> input_data,
219                                      absl::Span<U> output_data) {
220   if (const auto status = ValidateContainerSizeEqual(field_name, input_data,
221                                                      output_data.size());
222       !status.ok()) [[unlikely]] {
223     return status;
224   }
225 
226   for (int i = 0; i < input_data.size(); ++i) {
227     const auto status =
228         StaticCastIfInRange(field_name, input_data[i], output_data[i]);
229     if (!status.ok()) [[unlikely]] {
230       return status;
231     }
232   }
233   return absl::OkStatus();
234 }
235 }  // namespace iamf_tools
236 
237 #endif  // COMMON_UTILS_NUMERIC_UTILS_H_
238