RAVENNAKIT
Loading...
Searching...
No Matches
safe_int.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2/*
3 * Project: RAVENNAKIT (RAVENNA / AES67 / ST2110-30 SDK)
4 * Copyright (c) 2024-2025 Sound on Digital
5 *
6 * This file is part of RAVENNAKIT.
7 *
8 * RAVENNAKIT is dual-licensed:
9 * 1) Under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version (the "AGPL License"); and
12 * 2) Under a commercial license from Sound on Digital, for customers who
13 * cannot (or do not wish to) comply with the AGPL License terms.
14 *
15 * If you obtained this file under the AGPL License, you may redistribute it
16 * and/or modify it under the terms of the AGPL License. See the LICENSE
17 * file in the project root for details.
18 *
19 * For commercial licensing, support, and other inquiries, please visit:
20 *
21 * https://ravennakit.com
22 *
23 */
24
25#pragma once
26
28
29#include <limits>
30
31namespace rav {
32
34
39template<class T>
40class SafeInt {
41 public:
42 static_assert(std::is_integral_v<T>, "T must be an integral type");
43
44 SafeInt() = default;
45
50 explicit SafeInt(T value) : value_ {value} {}
51
56 T value() const {
57 return value_.value();
58 }
59
64 [[nodiscard]] SafeIntError error() const {
65 return value_.error();
66 }
67
71 tl::expected<T, SafeIntError> expected() const {
72 return value_;
73 }
74
81 template<class U>
83 if (!value_) {
84 return *this;
85 }
86 value_ = add(*value_, b);
87 return *this;
88 }
89
96 template<class U>
97 SafeInt operator+(U b) const {
98 SafeInt r = *this;
99 r += b;
100 return r;
101 }
102
109 template<class U>
111 if (!value_) {
112 return *this;
113 }
114 value_ = sub(*value_, b);
115 return *this;
116 }
117
124 template<class U>
125 SafeInt operator-(U b) const {
126 SafeInt r = *this;
127 r -= b;
128 return r;
129 }
130
137 template<class U>
139 if (!value_) {
140 return *this;
141 }
142 value_ = mul(*value_, b);
143 return *this;
144 }
145
152 template<class U>
153 SafeInt operator*(U b) const {
154 SafeInt r = *this;
155 r *= b;
156 return r;
157 }
158
165 template<class U>
167 if (!value_) {
168 return *this;
169 }
170 value_ = div(*value_, b);
171 return *this;
172 }
173
180 template<class U>
181 SafeInt operator/(U b) const {
182 SafeInt r = *this;
183 r /= b;
184 return r;
185 }
186
194 template<class U>
195 static tl::expected<T, SafeIntError> add(T a, U b) {
196 if (b > 0 && a > std::numeric_limits<T>::max() - b) {
197 return tl::unexpected(SafeIntError::overflow);
198 }
199 if (b < 0 && a < std::numeric_limits<T>::min() - b) {
200 return tl::unexpected(SafeIntError::underflow);
201 }
202 return a + b;
203 }
204
212 template<class U>
213 static tl::expected<T, SafeIntError> sub(T a, U b) {
214 if (b < 0 && a > std::numeric_limits<T>::max() + b) {
215 return tl::unexpected(SafeIntError::overflow);
216 }
217 if (b > 0 && a < std::numeric_limits<T>::min() + b) {
218 return tl::unexpected(SafeIntError::underflow);
219 }
220 return a - b;
221 }
222
230 template<class U>
231 static tl::expected<T, SafeIntError> mul(T a, U b) {
232 // Handle zero cases early
233 if (a == 0 || b == 0) {
234 return T {0};
235 }
236
237 // Check for overflow or underflow before performing the multiplication
238 if (a > 0) {
239 if (b > 0) {
240 if (a > std::numeric_limits<T>::max() / b) {
241 return tl::unexpected(SafeIntError::overflow);
242 }
243 } else {
244 if (b < std::numeric_limits<T>::min() / a) {
245 return tl::unexpected(SafeIntError::underflow);
246 }
247 }
248 } else {
249 if (b > 0) {
250 if (a < std::numeric_limits<T>::min() / b) {
251 return tl::unexpected(SafeIntError::underflow);
252 }
253 } else {
254 if (a < std::numeric_limits<T>::max() / b) {
255 return tl::unexpected(SafeIntError::overflow);
256 }
257 }
258 }
259
260 return a * b;
261 }
262
271 template<class U>
272 static tl::expected<T, SafeIntError> div(T a, U b) {
273 // Check for division by zero
274 if (b == 0) {
275 return tl::unexpected(SafeIntError::div_by_zero);
276 }
277
278 // Check for overflow when dividing the smallest negative value by -1
279 if constexpr (std::is_signed_v<T>) {
280 if (a == std::numeric_limits<T>::min() && b == -1) {
281 return tl::unexpected(SafeIntError::overflow);
282 }
283 }
284
285 return a / b;
286 }
287
288 private:
289 tl::expected<T, SafeIntError> value_ {};
290};
291
292// Convenience aliases
301
302} // namespace rav
A safe integer type that checks for overflow and underflow when performing arithmetic operations.
Definition safe_int.hpp:40
SafeInt(T value)
Constructs a safe_int from a value.
Definition safe_int.hpp:50
static tl::expected< T, SafeIntError > div(T a, U b)
Divides two values, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:272
SafeInt & operator+=(U b)
Adds a value to the safe_int, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:82
tl::expected< T, SafeIntError > expected() const
Definition safe_int.hpp:71
SafeInt()=default
SafeInt operator+(U b) const
Adds a value to the safe_int, returning a new safe_int with the result.
Definition safe_int.hpp:97
int8_t value() const
Definition safe_int.hpp:56
SafeInt operator-(U b) const
Subtracts a value from the safe_int, returning a new safe_int with the result.
Definition safe_int.hpp:125
SafeInt operator*(U b) const
Multiplies the safe_int by a scalar, returning a new safe_int with the result.
Definition safe_int.hpp:153
SafeInt & operator/=(U b)
Divides the safe_int by a scalar, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:166
SafeInt & operator-=(U b)
Subtracts a value from the safe_int, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:110
SafeInt & operator*=(U b)
Multiplies the safe_int by a scalar, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:138
SafeIntError error() const
Definition safe_int.hpp:64
static tl::expected< T, SafeIntError > add(T a, U b)
Adds two values, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:195
SafeInt operator/(U b) const
Divides the safe_int by a scalar, returning a new safe_int with the result.
Definition safe_int.hpp:181
static tl::expected< T, SafeIntError > mul(T a, U b)
Multiplies two values, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:231
static tl::expected< T, SafeIntError > sub(T a, U b)
Subtracts two values, returning an error if the operation would overflow or underflow.
Definition safe_int.hpp:213
SafeInt< uint8_t > SafeUint8
Definition safe_int.hpp:297
SafeInt< int64_t > SafeInt64
Definition safe_int.hpp:296
SafeInt< uint32_t > SafeUint32
Definition safe_int.hpp:299
SafeInt< int32_t > SafeInt32
Definition safe_int.hpp:295
SafeInt< int16_t > SafeInt16
Definition safe_int.hpp:294
SafeInt< uint64_t > SafeUint64
Definition safe_int.hpp:300
SafeInt< int8_t > SafeInt8
Definition safe_int.hpp:293
SafeIntError
Definition safe_int.hpp:33
SafeInt< uint16_t > SafeUint16
Definition safe_int.hpp:298