blob: 0bc0fbfac473dc2d38b421a5dff1fbfb9b7c639a [file] [log] [blame]
Herb Derby35ae65d2017-08-11 14:00:50 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkSafeMath_DEFINED
9#define SkSafeMath_DEFINED
10
Mike Reed267eccc2018-02-21 15:55:14 -050011#include "SkTypes.h"
12
Herb Derby35ae65d2017-08-11 14:00:50 -040013// SkSafeMath always check that a series of operations do not overflow.
14// This must be correct for all platforms, because this is a check for safety at runtime.
15
16class SkSafeMath {
17public:
18 SkSafeMath() = default;
19
20 bool ok() const { return fOK; }
21 explicit operator bool() const { return fOK; }
22
23 size_t mul(size_t x, size_t y) {
24 return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y);
25 }
26
27 size_t add(size_t x, size_t y) {
28 size_t result = x + y;
29 fOK &= result >= x;
30 return result;
31 }
32
Mike Reed67b190b2017-11-21 13:34:40 -050033 /**
34 * Return a + b, unless this result is an overflow/underflow. In those cases, fOK will
35 * be set to false, and it is undefined what this returns.
36 */
37 int addInt(int a, int b) {
38 if (b < 0 && a < std::numeric_limits<int>::min() - b) {
39 fOK = false;
40 return a;
41 } else if (b > 0 && a > std::numeric_limits<int>::max() - b) {
42 fOK = false;
43 return a;
44 }
45 return a + b;
46 }
47
Florin Malitad923a712017-11-22 10:11:12 -050048 size_t alignUp(size_t x, size_t alignment) {
49 SkASSERT(alignment && !(alignment & (alignment - 1)));
50 return add(x, alignment - 1) & ~(alignment - 1);
51 }
52
Mike Reed33f38b02018-02-14 13:58:06 -050053 template <typename T> T castTo(size_t value) {
54 if (!SkTFitsIn<T>(value)) {
55 fOK = false;
56 }
57 return static_cast<T>(value);
58 }
59
Mike Reed5c68dce2017-12-21 21:54:32 -050060 // These saturate to their results
61 static size_t Add(size_t x, size_t y);
62 static size_t Mul(size_t x, size_t y);
Mike Reedbaafcdc2018-01-22 13:34:53 -050063 static size_t Align4(size_t x) {
64 SkSafeMath safe;
65 return safe.alignUp(x, 4);
66 }
Mike Reed5c68dce2017-12-21 21:54:32 -050067
Herb Derby35ae65d2017-08-11 14:00:50 -040068private:
69 uint32_t mul32(uint32_t x, uint32_t y) {
70 uint64_t bx = x;
71 uint64_t by = y;
72 uint64_t result = bx * by;
73 fOK &= result >> 32 == 0;
74 return result;
75 }
76
77 uint64_t mul64(uint64_t x, uint64_t y) {
78 if (x <= std::numeric_limits<uint64_t>::max() >> 32
79 && y <= std::numeric_limits<uint64_t>::max() >> 32) {
80 return x * y;
81 } else {
82 auto hi = [](uint64_t x) { return x >> 32; };
83 auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
84
85 uint64_t lx_ly = lo(x) * lo(y);
86 uint64_t hx_ly = hi(x) * lo(y);
87 uint64_t lx_hy = lo(x) * hi(y);
88 uint64_t hx_hy = hi(x) * hi(y);
89 uint64_t result = 0;
90 result = this->add(lx_ly, (hx_ly << 32));
91 result = this->add(result, (lx_hy << 32));
92 fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0;
93
94 #if defined(SK_DEBUG) && defined(__clang__) && defined(__x86_64__)
95 auto double_check = (unsigned __int128)x * y;
96 SkASSERT(result == (double_check & 0xFFFFFFFFFFFFFFFF));
97 SkASSERT(!fOK || (double_check >> 64 == 0));
98 #endif
99
100 return result;
101 }
102 }
103 bool fOK = true;
104};
105
106#endif//SkSafeMath_DEFINED