1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "NumericValue.h"
18 
19 #include <cmath>
20 #include <functional>
21 #include <limits>
22 #include <string>
23 #include <utility>
24 #include <variant>
25 
26 #include "HistogramValue.h"
27 
28 namespace android {
29 namespace os {
30 namespace statsd {
31 namespace {
32 
33 // std::variant uses the visitor pattern to interact with stored types via std::visit, which applies
34 // a Callable (a function object) that accepts all combination of types from the variant. Here, the
35 // Callables are implemented as structs with operator() overloads for each combination of types from
36 // the variant in NumericValue.
37 
38 // Templated visitor for binary operations involving two NumericValues
39 // Used for implementing operator+= and operator-= for NumericValue.
40 template <typename BinaryOp>
41 class BinaryOperationVisitor {
42 public:
BinaryOperationVisitor(BinaryOp && op)43     constexpr explicit BinaryOperationVisitor(BinaryOp&& op) : mOp(std::forward<BinaryOp>(op)) {
44     }
45 
operator ()(std::monostate,std::monostate) const46     void operator()(std::monostate, std::monostate) const {
47     }
48 
49     template <typename V>
operator ()(V & lhs,const V & rhs) const50     void operator()(V& lhs, const V& rhs) const {
51         lhs = mOp(lhs, rhs);
52     }
53 
operator ()(auto,auto) const54     void operator()(auto, auto) const {
55     }
56 
57 private:
58     const BinaryOp mOp;
59 };
60 constexpr BinaryOperationVisitor subtract(std::minus{});
61 constexpr BinaryOperationVisitor add(std::plus{});
62 
63 // Visitor for printing type information currently stored in the NumericValue variant.
64 struct ToStringVisitor {
operator ()android::os::statsd::__anoned9e2b6d0111::ToStringVisitor65     std::string operator()(int64_t value) const {
66         return std::to_string(value) + "[L]";
67     }
68 
operator ()android::os::statsd::__anoned9e2b6d0111::ToStringVisitor69     std::string operator()(double value) const {
70         return std::to_string(value) + "[D]";
71     }
72 
operator ()android::os::statsd::__anoned9e2b6d0111::ToStringVisitor73     std::string operator()(const HistogramValue& value) const {
74         return value.toString();
75     }
76 
operator ()android::os::statsd::__anoned9e2b6d0111::ToStringVisitor77     std::string operator()(auto) const {
78         return "[UNKNOWN]";
79     }
80 };
81 
82 // Visitor for determining whether the NumericValue variant stores a 0.
83 struct IsZeroVisitor {
operator ()android::os::statsd::__anoned9e2b6d0111::IsZeroVisitor84     bool operator()(int64_t value) const {
85         return value == 0;
86     }
87 
operator ()android::os::statsd::__anoned9e2b6d0111::IsZeroVisitor88     bool operator()(double value) const {
89         return fabs(value) <= std::numeric_limits<double>::epsilon();
90     }
91 
operator ()android::os::statsd::__anoned9e2b6d0111::IsZeroVisitor92     bool operator()(const HistogramValue& value) const {
93         return value.isEmpty();
94     }
95 
96     // "Empty" variant does not store 0.
operator ()android::os::statsd::__anoned9e2b6d0111::IsZeroVisitor97     bool operator()(std::monostate) const {
98         return false;
99     }
100 };
101 
102 struct GetSizeVisitor {
operator ()android::os::statsd::__anoned9e2b6d0111::GetSizeVisitor103     size_t operator()(const HistogramValue& value) const {
104         return value.getSize();
105     }
106 
operator ()android::os::statsd::__anoned9e2b6d0111::GetSizeVisitor107     size_t operator()(const auto& value) const {
108         return sizeof(value);
109     }
110 };
111 
112 }  // anonymous namespace
113 
toString() const114 std::string NumericValue::toString() const {
115     return std::visit(ToStringVisitor{}, mData);
116 }
117 
reset()118 void NumericValue::reset() {
119     mData.emplace<std::monostate>(std::monostate{});
120 }
121 
122 template <typename V>
is() const123 bool NumericValue::is() const {
124     return std::holds_alternative<V>(mData);
125 }
126 template bool NumericValue::is<int64_t>() const;
127 template bool NumericValue::is<double>() const;
128 template bool NumericValue::is<HistogramValue>() const;
129 
hasValue() const130 bool NumericValue::hasValue() const {
131     return !is<std::monostate>();
132 }
133 
134 template <typename V>
getValue()135 V& NumericValue::getValue() {
136     return std::get<V>(mData);
137 }
138 template int64_t& NumericValue::getValue<int64_t>();
139 template double& NumericValue::getValue<double>();
140 template HistogramValue& NumericValue::getValue<HistogramValue>();
141 
142 template <typename V>
getValue() const143 const V& NumericValue::getValue() const {
144     return std::get<V>(mData);
145 }
146 template const int64_t& NumericValue::getValue<int64_t>() const;
147 template const double& NumericValue::getValue<double>() const;
148 template const HistogramValue& NumericValue::getValue<HistogramValue>() const;
149 
150 template <typename V>
getValueOrDefault(V & defaultValue)151 V& NumericValue::getValueOrDefault(V& defaultValue) {
152     return is<V>() ? getValue<V>() : defaultValue;
153 }
154 template int64_t& NumericValue::getValueOrDefault<int64_t>(int64_t& defaultValue);
155 template double& NumericValue::getValueOrDefault<double>(double& defaultValue);
156 template HistogramValue& NumericValue::getValueOrDefault<HistogramValue>(
157         HistogramValue& defaultValue);
158 
159 template <typename V>
getValueOrDefault(const V & defaultValue) const160 const V& NumericValue::getValueOrDefault(const V& defaultValue) const {
161     return is<V>() ? getValue<V>() : defaultValue;
162 }
163 template const int64_t& NumericValue::getValueOrDefault<int64_t>(const int64_t& defaultValue) const;
164 template const double& NumericValue::getValueOrDefault<double>(const double& defaultValue) const;
165 template const HistogramValue& NumericValue::getValueOrDefault<HistogramValue>(
166         const HistogramValue& defaultValue) const;
167 
isZero() const168 bool NumericValue::isZero() const {
169     return std::visit(IsZeroVisitor{}, mData);
170 }
171 
getSize() const172 size_t NumericValue::getSize() const {
173     return std::visit(GetSizeVisitor{}, mData);
174 }
175 
operator +=(const NumericValue & rhs)176 NumericValue& NumericValue::operator+=(const NumericValue& rhs) {
177     std::visit(add, mData, rhs.mData);
178     return *this;
179 }
180 
operator +(NumericValue lhs,const NumericValue & rhs)181 NumericValue operator+(NumericValue lhs, const NumericValue& rhs) {
182     lhs += rhs;
183     return lhs;
184 }
185 
operator -=(const NumericValue & rhs)186 NumericValue& NumericValue::operator-=(const NumericValue& rhs) {
187     std::visit(subtract, mData, rhs.mData);
188     return *this;
189 }
190 
operator -(NumericValue lhs,const NumericValue & rhs)191 NumericValue operator-(NumericValue lhs, const NumericValue& rhs) {
192     lhs -= rhs;
193     return lhs;
194 }
195 
operator ==(const NumericValue & lhs,const NumericValue & rhs)196 bool operator==(const NumericValue& lhs, const NumericValue& rhs) {
197     return lhs.mData == rhs.mData;
198 }
199 
operator !=(const NumericValue & lhs,const NumericValue & rhs)200 bool operator!=(const NumericValue& lhs, const NumericValue& rhs) {
201     return !(lhs == rhs);
202 }
203 
operator <(const NumericValue & lhs,const NumericValue & rhs)204 bool operator<(const NumericValue& lhs, const NumericValue& rhs) {
205     return lhs.mData < rhs.mData;
206 }
207 
operator >(const NumericValue & lhs,const NumericValue & rhs)208 bool operator>(const NumericValue& lhs, const NumericValue& rhs) {
209     return rhs < lhs;
210 }
211 
operator <=(const NumericValue & lhs,const NumericValue & rhs)212 bool operator<=(const NumericValue& lhs, const NumericValue& rhs) {
213     return !(lhs > rhs);
214 }
215 
operator >=(const NumericValue & lhs,const NumericValue & rhs)216 bool operator>=(const NumericValue& lhs, const NumericValue& rhs) {
217     return !(lhs < rhs);
218 }
219 
220 }  // namespace statsd
221 }  // namespace os
222 }  // namespace android
223