16e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim#include "dng_safe_arithmetic.h"
26e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
31754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener#include <cmath>
46e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim#include <limits>
56e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
66e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim#include "dng_exceptions.h"
76e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
86e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// Implementation of safe integer arithmetic follows guidelines from
96e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// https://www.securecoding.cert.org/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
106e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// and
116e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow
126e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
136e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimnamespace {
146e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
156e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// Template functions for safe arithmetic. These functions are not exposed in
166e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// the header for the time being to avoid having to add checks for the various
176e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// constraints on the template argument (e.g. that it is integral and possibly
186e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// signed or unsigned only). This should be done using a static_assert(), but
196e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// we want to be portable to pre-C++11 compilers.
206e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
216e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// Returns the result of adding arg1 and arg2 if it will fit in a T (where T is
226e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// a signed or unsigned integer type). Otherwise, throws a dng_exception with
236e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// error code dng_error_unknown.
246e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimtemplate <class T>
256e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan HakimT SafeAdd(T arg1, T arg2) {
266e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // The condition is reformulated relative to the version on
276e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // www.securecoding.cert.org to check for valid instead of invalid cases. It
286e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // seems safer to enumerate the valid cases (and potentially miss one) than
296e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // enumerate the invalid cases.
306e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // If T is an unsigned type, the second half of the condition always evaluates
316e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // to false and will presumably be compiled out by the compiler.
326e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if ((arg1 >= 0 && arg2 <= std::numeric_limits<T>::max() - arg1) ||
336e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim      (arg1 < 0 && arg2 >= std::numeric_limits<T>::min() - arg1)) {
346e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return arg1 + arg2;
356e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
366e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    ThrowProgramError("Arithmetic overflow");
37c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
386e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
396e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
406e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
416e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// Returns the result of multiplying arg1 and arg2 if it will fit in a T (where
426e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// T is an unsigned integer type). Otherwise, throws a dng_exception with error
436e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim// code dng_error_unknown.
446e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimtemplate <class T>
456e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan HakimT SafeUnsignedMult(T arg1, T arg2) {
466e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (arg1 == 0 || arg2 <= std::numeric_limits<T>::max() / arg1) {
476e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return arg1 * arg2;
486e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
496e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    ThrowProgramError("Arithmetic overflow");
50c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
516e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
526e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
536e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
546e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}  // namespace
556e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
566e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeInt32Add(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) {
576e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  try {
586e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = SafeInt32Add(arg1, arg2);
596e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
606e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } catch (const dng_exception &) {
616e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
626e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
636e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
646e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
656e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::int32_t SafeInt32Add(std::int32_t arg1, std::int32_t arg2) {
666e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeAdd<std::int32_t>(arg1, arg2);
676e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
686e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
696e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::int64_t SafeInt64Add(std::int64_t arg1, std::int64_t arg2) {
706e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeAdd<std::int64_t>(arg1, arg2);
716e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
726e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
736e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2,
746e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                   std::uint32_t *result) {
756e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  try {
766e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = SafeUint32Add(arg1, arg2);
776e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
786e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } catch (const dng_exception &) {
796e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
806e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
816e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
826e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
836e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::uint32_t SafeUint32Add(std::uint32_t arg1, std::uint32_t arg2) {
846e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeAdd<std::uint32_t>(arg1, arg2);
856e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
866e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
87c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::uint64_t SafeUint64Add(std::uint64_t arg1, std::uint64_t arg2) {
88c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  return SafeAdd<std::uint64_t>(arg1, arg2);
89c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
90c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
916e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeInt32Sub(std::int32_t arg1, std::int32_t arg2, std::int32_t *result) {
926e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if ((arg2 >= 0 && arg1 >= std::numeric_limits<int32_t>::min() + arg2) ||
936e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim      (arg2 < 0 && arg1 <= std::numeric_limits<int32_t>::max() + arg2)) {
946e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = arg1 - arg2;
956e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
966e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
976e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
986e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
996e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1006e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1016e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::int32_t SafeInt32Sub(std::int32_t arg1, std::int32_t arg2) {
1026e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  std::int32_t result = 0;
1036e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1046e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (!SafeInt32Sub(arg1, arg2, &result)) {
1056e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    ThrowProgramError("Arithmetic overflow");
1066e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
1076e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1086e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return result;
1096e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1106e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
111c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::uint32_t SafeUint32Sub(std::uint32_t arg1, std::uint32_t arg2) {
112c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  if (arg1 >= arg2) {
113c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return arg1 - arg2;
114c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  } else {
115c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    ThrowProgramError("Arithmetic overflow");
116c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
117c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  }
118c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
119c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
1206e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
1216e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                    std::uint32_t *result) {
1226e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  try {
1236e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = SafeUint32Mult(arg1, arg2);
1246e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
1256e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } catch (const dng_exception &) {
1266e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
1276e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
1286e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1296e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1306e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3,
1316e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                    std::uint32_t *result) {
1326e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  try {
1336e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = SafeUint32Mult(arg1, arg2, arg3);
1346e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
1356e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } catch (const dng_exception &) {
1366e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
1376e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
1386e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1396e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1406e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2, std::uint32_t arg3,
1416e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                    std::uint32_t arg4, std::uint32_t *result) {
1426e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  try {
1436e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    *result = SafeUint32Mult(arg1, arg2, arg3, arg4);
1446e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return true;
1456e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } catch (const dng_exception &) {
1466e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return false;
1476e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
1486e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1496e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1506e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2) {
1516e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeUnsignedMult<std::uint32_t>(arg1, arg2);
1526e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1536e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1546e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
1556e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                             std::uint32_t arg3) {
1566e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeUint32Mult(SafeUint32Mult(arg1, arg2), arg3);
1576e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1586e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1596e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::uint32_t SafeUint32Mult(std::uint32_t arg1, std::uint32_t arg2,
1606e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                             std::uint32_t arg3, std::uint32_t arg4) {
1616e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeUint32Mult(SafeUint32Mult(arg1, arg2, arg3), arg4);
1626e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1636e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
164c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::int32_t SafeInt32Mult(std::int32_t arg1, std::int32_t arg2) {
165c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  const std::int64_t tmp =
166c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener      static_cast<std::int64_t>(arg1) * static_cast<std::int64_t>(arg2);
167089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  if (tmp >= std::numeric_limits<std::int32_t>::min() &&
168089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      tmp <= std::numeric_limits<std::int32_t>::max()) {
169c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return static_cast<std::int32_t>(tmp);
170c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  } else {
171c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    ThrowProgramError("Arithmetic overflow");
172c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();
173c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  }
174c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
175c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
1766e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::size_t SafeSizetMult(std::size_t arg1, std::size_t arg2) {
1776e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  return SafeUnsignedMult<std::size_t>(arg1, arg2);
1786e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
1796e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
180c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienernamespace dng_internal {
181c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
182c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::int64_t SafeInt64MultSlow(std::int64_t arg1, std::int64_t arg2) {
1836e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  bool overflow = true;
1846e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
1856e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (arg1 > 0) {
1866e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    if (arg2 > 0) {
187089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      overflow = (arg1 > std::numeric_limits<std::int64_t>::max() / arg2);
1886e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    } else {
189089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      overflow = (arg2 < std::numeric_limits<std::int64_t>::min() / arg1);
1906e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    }
1916e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
1926e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    if (arg2 > 0) {
193089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      overflow = (arg1 < std::numeric_limits<std::int64_t>::min() / arg2);
1946e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    } else {
195089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      overflow = (arg1 != 0 &&
196089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin                  arg2 < std::numeric_limits<std::int64_t>::max() / arg1);
1976e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    }
1986e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
1996e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2006e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (overflow) {
2016e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    ThrowProgramError("Arithmetic overflow");
202c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
2036e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
2046e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return arg1 * arg2;
2056e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
2066e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
2076e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
208c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}  // namespace dng_internal
209c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
2106e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimstd::uint32_t SafeUint32DivideUp(std::uint32_t arg1, std::uint32_t arg2) {
2116e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // It might seem more intuitive to implement this function simply as
2126e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  //
2136e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  //   return arg2 == 0 ? 0 : (arg1 + arg2 - 1) / arg2;
2146e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  //
2156e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  // but the expression "arg1 + arg2" can wrap around.
2166e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2176e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (arg2 == 0) {
2186e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    ThrowProgramError("Division by zero");
219c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
2206e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else if (arg1 == 0) {
2216e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    // If arg1 is zero, return zero to avoid wraparound in the expression
2226e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    //   "arg1 - 1" below.
2236e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return 0;
2246e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
2256e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim    return (arg1 - 1) / arg2 + 1;
2266e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
2276e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
2286e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2296e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool RoundUpUint32ToMultiple(std::uint32_t val, std::uint32_t multiple_of,
2306e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim                             std::uint32_t *result) {
231c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  try {
232c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    *result = RoundUpUint32ToMultiple(val, multiple_of);
233c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return true;
234c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  } catch (const dng_exception &) {
23529c7498fabe2e3c87a85b487bfe9d783c401e1f0Yujie Qin    return false;
236a9912faa4ebc5100cfb7f03718304a315edf32fbFlorian Kriener  }
237c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
238c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
239c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::uint32_t RoundUpUint32ToMultiple(std::uint32_t val,
240c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener                                      std::uint32_t multiple_of) {
241c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  if (multiple_of == 0) {
242c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    ThrowProgramError("multiple_of is zero in RoundUpUint32ToMultiple");
243c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  }
2446e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2456e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  const std::uint32_t remainder = val % multiple_of;
2466e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (remainder == 0) {
247c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return val;
2486e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  } else {
249c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return SafeUint32Add(val, multiple_of - remainder);
2506e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
2516e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
2526e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2536e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakimbool ConvertUint32ToInt32(std::uint32_t val, std::int32_t *result) {
254c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  try {
255c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    *result = ConvertUint32ToInt32(val);
256c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return true;
257c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  } catch (const dng_exception &) {
258c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return false;
259c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  }
260c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
261c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
262c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::int32_t ConvertUint32ToInt32(std::uint32_t val) {
2636e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  const std::uint32_t kInt32MaxAsUint32 =
2646e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim      static_cast<std::uint32_t>(std::numeric_limits<std::int32_t>::max());
2656e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim
2666e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  if (val <= kInt32MaxAsUint32) {
267c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return static_cast<std::int32_t>(val);
268a9912faa4ebc5100cfb7f03718304a315edf32fbFlorian Kriener  } else {
269c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    ThrowProgramError("Arithmetic overflow");
270c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
271c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  }
272c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener}
273c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener
274c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Krienerstd::int32_t ConvertDoubleToInt32(double val) {
275089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  const double kMin =
276089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      static_cast<double>(std::numeric_limits<std::int32_t>::min());
277089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  const double kMax =
278089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      static_cast<double>(std::numeric_limits<std::int32_t>::max());
279c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  // NaNs will fail this test; they always compare false.
280c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  if (val > kMin - 1.0 && val < kMax + 1.0) {
281c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    return static_cast<std::int32_t>(val);
282c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener  } else {
283c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    ThrowProgramError("Argument not in range in ConvertDoubleToInt32");
284c89552e7ed1e0ea69c69addf2bb5de011188d297Florian Kriener    abort();  // Never reached.
2856e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim  }
2866e09dfbee0b1643a5cb2b32d0399c1a0c69551a0Kinan Hakim}
287089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin
288089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qinstd::uint32_t ConvertDoubleToUint32(double val) {
289089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  const double kMax =
290089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin      static_cast<double>(std::numeric_limits<std::uint32_t>::max());
291089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  // NaNs will fail this test; they always compare false.
292089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  if (val >= 0.0 && val < kMax + 1.0) {
293089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin    return static_cast<std::uint32_t>(val);
294089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  } else {
295089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin    ThrowProgramError("Argument not in range in ConvertDoubleToUint32");
296089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin    abort();  // Never reached.
297089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin  }
298089babbee3f41a18e7940fa86fe4f3cfa45bc6eaYujie Qin}
2991754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener
3001754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Krienerfloat ConvertDoubleToFloat(double val) {
3011754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener  const double kMax = std::numeric_limits<float>::max();
3021754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener  if (val > kMax) {
3031754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    return std::numeric_limits<float>::infinity();
3041754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener  } else if (val < -kMax) {
3051754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    return -std::numeric_limits<float>::infinity();
3061754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener  } else {
3071754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    // The cases that end up here are:
3081754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    // - values in [-kMax, kMax]
3091754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    // - NaN (because it always compares false)
3101754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener    return static_cast<float>(val);
3111754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener  }
3121754202ed3e25b98179a4f377f0bdd605d9a157aFlorian Kriener}
313