1+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2+ /*
3+ * Copyright 2020 Couchbase, Inc
4+ *
5+ * Licensed under the Apache License, Version 2.0 (the "License");
6+ * you may not use this file except in compliance with the License.
7+ * You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+
18+ #pragma once
19+
20+ #include < folly/lang/Assume.h>
21+ #include < gsl/gsl>
22+
23+ #include < array>
24+ #include < ratio>
25+ #include < string>
26+
27+ namespace cb ::stats {
28+
29+ // Enum of the relevant base units used by convention in Prometheus
30+ enum class BaseUnit {
31+ None, // used for text stats where units aren't relevant
32+ Count, // Generic count which does not meet a better unit
33+ Seconds,
34+ Bytes,
35+ Ratio,
36+ };
37+
38+ /* *
39+ * Type representing a unit (kilobytes, microseconds etc.).
40+ * This is encoded as a BaseUnit (see above) and a scaling factor stored as
41+ * two integers (see std::ratio).
42+ *
43+ * Thus,
44+ * kilobytes -> bytes, 1000/1
45+ * microseconds -> seconds, 1/1000000
46+ *
47+ * Given a numerical value in this unit, the scaling factor indicates
48+ * how to convert the value back to the base unit.
49+ *
50+ * 10 kilobytes
51+ * 10 * 1000 / 1
52+ * -> 10000 bytes
53+ *
54+ * This can be performed using the toBaseUnit method -
55+ *
56+ * Unit(std::kilo{}, BaseUnit::Bytes).toBaseUnit(10) == 10000
57+ *
58+ * Units will usually be used through the constexpr values
59+ * in namespace units
60+ *
61+ * units::kilobytes.toBaseUnit(10) == 10000
62+ */
63+ class Unit {
64+ public:
65+ explicit constexpr Unit (BaseUnit baseUnit)
66+ : Unit(std::ratio<1 >{}, baseUnit) {
67+ }
68+
69+ template <class RatioType >
70+ constexpr Unit (RatioType ratio, BaseUnit baseUnit)
71+ : numerator(gsl::narrow_cast<int64_t >(RatioType::num)),
72+ denominator(gsl::narrow_cast<int64_t >(RatioType::den)),
73+ baseUnit(baseUnit) {
74+ }
75+
76+ /* *
77+ * Scale a value of the current unit (e.g., milliseconds) to the base
78+ * unit (e.g., seconds).
79+ */
80+ [[nodiscard]] constexpr double toBaseUnit (double value) const {
81+ return (value * numerator) / denominator;
82+ }
83+
84+ [[nodiscard]] std::string_view getSuffix () const {
85+ using namespace std ::string_view_literals;
86+ switch (baseUnit) {
87+ case BaseUnit::None:
88+ case BaseUnit::Count:
89+ return " " sv;
90+ case BaseUnit::Seconds:
91+ return " _seconds" sv;
92+ case BaseUnit::Bytes:
93+ return " _bytes" sv;
94+ case BaseUnit::Ratio:
95+ return " _ratio" sv;
96+ }
97+ folly::assume_unreachable ();
98+ }
99+
100+ private:
101+ int64_t numerator;
102+ int64_t denominator;
103+ BaseUnit baseUnit;
104+ };
105+
106+ namespace units {
107+ constexpr Unit none{std::ratio<1 >{}, BaseUnit::None};
108+
109+ constexpr Unit count{std::ratio<1 >{}, BaseUnit::Count};
110+
111+ // floating point between 0 and 1, this is already in the correct base unit
112+ constexpr Unit ratio{std::ratio<1 >{}, BaseUnit::Ratio};
113+ // percents should be scaled down to ratios for Prometheus
114+ constexpr Unit percent{std::ratio<1 , 100 >{}, BaseUnit::Ratio};
115+
116+ // time units
117+ constexpr Unit nanoseconds{std::nano{}, BaseUnit::Seconds};
118+ constexpr Unit microseconds{std::micro{}, BaseUnit::Seconds};
119+ constexpr Unit milliseconds{std::milli{}, BaseUnit::Seconds};
120+ constexpr Unit seconds{std::ratio<1 >{}, BaseUnit::Seconds};
121+ constexpr Unit minutes{std::ratio<60 >{}, BaseUnit::Seconds};
122+ constexpr Unit hours{std::ratio<60 * 60 >{}, BaseUnit::Seconds};
123+ constexpr Unit days{std::ratio<60 * 60 * 24 >{}, BaseUnit::Seconds};
124+
125+ // Note: std ratio definitions are powers of 10, not 2
126+ // e.g., kilo = std::ratio<1000, 1>
127+ // so 1 kilobyte = 1000 bytes
128+ // if powers of 2 are explicitly needed, IEC prefixes
129+ // (kibi, gibi, tebi) could easily be defined.
130+
131+ // byte units
132+ constexpr Unit bits{std::ratio<1 , 8 >{}, BaseUnit::Bytes};
133+ constexpr Unit bytes{std::ratio<1 >{}, BaseUnit::Bytes};
134+ constexpr Unit kilobytes{std::kilo{}, BaseUnit::Bytes};
135+ constexpr Unit megabytes{std::mega{}, BaseUnit::Bytes};
136+ constexpr Unit gigabytes{std::giga{}, BaseUnit::Bytes};
137+ } // namespace units
138+ } // namespace cb::stats
0 commit comments