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 #include "iamf/common/utils/numeric_utils.h"
13
14 #include <bit>
15 #include <cmath>
16 #include <cstddef>
17 #include <cstdint>
18 #include <limits>
19 #include <vector>
20
21 #include "absl/log/check.h"
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "absl/types/span.h"
27
28 namespace iamf_tools {
29
AddUint32CheckOverflow(uint32_t x_1,uint32_t x_2,uint32_t & result)30 absl::Status AddUint32CheckOverflow(uint32_t x_1, uint32_t x_2,
31 uint32_t& result) {
32 // Add in the payload size.
33 const uint64_t sum = static_cast<uint64_t>(x_1) + static_cast<uint64_t>(x_2);
34 // Check if this would overflow as a `uint32_t`.
35 if (sum > std::numeric_limits<uint32_t>::max()) {
36 return absl::InvalidArgumentError(
37 "Result of AddUint32CheckOverflow would overflow a uint32_t.");
38 }
39 result = static_cast<uint32_t>(sum);
40 return absl::OkStatus();
41 }
42
FloatToQ7_8(float value,int16_t & result)43 absl::Status FloatToQ7_8(float value, int16_t& result) {
44 // Q7.8 format can represent values in the range [-2^7, 2^7 - 2^-8].
45 if (std::isnan(value) || value < -128 || (128.0 - 1.0 / 256.0) < value) {
46 return absl::UnknownError(absl::StrCat(
47 "Value, ", value, " cannot be represented in Q7.8 format."));
48 }
49 result = static_cast<int16_t>(value * (1 << 8));
50 return absl::OkStatus();
51 }
52
Q7_8ToFloat(int16_t value)53 float Q7_8ToFloat(int16_t value) {
54 return static_cast<float>(value) * 1.0f / 256.0f;
55 }
56
FloatToQ0_8(float value,uint8_t & result)57 absl::Status FloatToQ0_8(float value, uint8_t& result) {
58 // Q0.8 format can represent values in the range [0, 1 - 2^-8].
59 if (std::isnan(value) || value < 0 || 1 <= value) {
60 return absl::UnknownError(absl::StrCat(
61 "Value, ", value, " cannot be represented in Q0.8 format."));
62 }
63
64 result = static_cast<uint8_t>(value * (1 << 8));
65 return absl::OkStatus();
66 }
67
Q0_8ToFloat(uint8_t value)68 float Q0_8ToFloat(uint8_t value) {
69 return static_cast<float>(value) * 1.0f / 256.0f;
70 }
71
LittleEndianBytesToInt32(absl::Span<const uint8_t> bytes,int32_t & output)72 absl::Status LittleEndianBytesToInt32(absl::Span<const uint8_t> bytes,
73 int32_t& output) {
74 // If we have bytes A, B, C, D, then we need to read them as:
75 // (D << 24) | (C << 16) | (B << 8) | A
76 // If we have less than four bytes, e.g. two bytes, we would read them as:
77 // (B << 8) | A
78 // with the upper bits filled with the sign bit.
79 const size_t num_bytes = bytes.size();
80 if (num_bytes > 4 || num_bytes < 1) {
81 return absl::InvalidArgumentError("Need [1, 4] bytes to make an int32_t");
82 }
83 int32_t result = 0;
84 for (int i = 0; i < bytes.size(); ++i) {
85 const auto shift = 8 * ((4 - num_bytes) + i);
86 result |= static_cast<int32_t>(bytes[i]) << shift;
87 }
88 output = result;
89 return absl::OkStatus();
90 }
91
BigEndianBytesToInt32(absl::Span<const uint8_t> bytes,int32_t & output)92 absl::Status BigEndianBytesToInt32(absl::Span<const uint8_t> bytes,
93 int32_t& output) {
94 // If we have bytes A, B, C, D, then we need to read them as:
95 // (A << 24) | (B << 16) | (C << 8) | D
96 // If we have less than four bytes, e.g. two bytes, we would read them as:
97 // (A << 8) | B
98 // with the upper bits filled with the sign bit.
99 auto reversed_bytes = std::vector<uint8_t>(bytes.rbegin(), bytes.rend());
100 return LittleEndianBytesToInt32(reversed_bytes, output);
101 }
102
ClipDoubleToInt32(double input,int32_t & output)103 absl::Status ClipDoubleToInt32(double input, int32_t& output) {
104 if (std::isnan(input)) {
105 return absl::InvalidArgumentError("Input is NaN.");
106 }
107
108 if (input > std::numeric_limits<int32_t>::max()) {
109 output = std::numeric_limits<int32_t>::max();
110 } else if (input < std::numeric_limits<int32_t>::min()) {
111 output = std::numeric_limits<int32_t>::min();
112 } else {
113 output = static_cast<int32_t>(input);
114 }
115
116 return absl::OkStatus();
117 }
118
IsNativeBigEndian()119 bool IsNativeBigEndian() {
120 if (std::endian::native == std::endian::big) {
121 return true;
122 } else if (std::endian::native == std::endian::little) {
123 return false;
124 } else {
125 CHECK(false) << "Mixed-endian systems are not supported.";
126 }
127 }
128
129 } // namespace iamf_tools
130