Apache Mesos
duration.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_DURATION_HPP__
14 #define __STOUT_DURATION_HPP__
15 
16 #include <ctype.h> // For 'isdigit'.
17 
18 // For 'timeval'.
19 #ifndef __WINDOWS__
20 #include <sys/time.h>
21 #endif // __WINDOWS__
22 
23 #include <iomanip>
24 #include <iostream>
25 #include <limits>
26 #include <string>
27 
28 #include "error.hpp"
29 #include "numify.hpp"
30 #include "try.hpp"
31 
32 class Duration
33 {
34 public:
35  static Try<Duration> parse(const std::string& s)
36  {
37  // TODO(benh): Support negative durations (i.e., starts with '-').
38  size_t index = 0;
39  while (index < s.size()) {
40  if (isdigit(s[index]) || s[index] == '.') {
41  index++;
42  continue;
43  }
44 
45  Try<double> value = numify<double>(s.substr(0, index));
46 
47  if (value.isError()) {
48  return Error(value.error());
49  }
50 
51  const std::string unit = s.substr(index);
52 
53  int64_t factor;
54  if (unit == "ns") {
55  factor = NANOSECONDS;
56  } else if (unit == "us") {
57  factor = MICROSECONDS;
58  } else if (unit == "ms") {
59  factor = MILLISECONDS;
60  } else if (unit == "secs") {
61  factor = SECONDS;
62  } else if (unit == "mins") {
63  factor = MINUTES;
64  } else if (unit == "hrs") {
65  factor = HOURS;
66  } else if (unit == "days") {
67  factor = DAYS;
68  } else if (unit == "weeks") {
69  factor = WEEKS;
70  } else {
71  return Error(
72  "Unknown duration unit '" + unit + "'; supported units are"
73  " 'ns', 'us', 'ms', 'secs', 'mins', 'hrs', 'days', and 'weeks'");
74  }
75 
76  double nanos = value.get() * factor;
77  if (nanos > max().nanos || nanos < min().nanos) {
78  return Error(
79  "Argument out of the range that a Duration can represent due"
80  " to int64_t's size limit");
81  }
82 
83  return Duration(value.get(), factor);
84  }
85 
86  return Error("Invalid duration '" + s + "'");
87  }
88 
89  static Try<Duration> create(double seconds);
90 
91  constexpr Duration() : nanos(0) {}
92 
93  explicit Duration(const timeval& t)
94  {
95  nanos = t.tv_sec * SECONDS + t.tv_usec * MICROSECONDS;
96  }
97 
98  int64_t ns() const { return nanos; }
99  double us() const { return static_cast<double>(nanos) / MICROSECONDS; }
100  double ms() const { return static_cast<double>(nanos) / MILLISECONDS; }
101  double secs() const { return static_cast<double>(nanos) / SECONDS; }
102  double mins() const { return static_cast<double>(nanos) / MINUTES; }
103  double hrs() const { return static_cast<double>(nanos) / HOURS; }
104  double days() const { return static_cast<double>(nanos) / DAYS; }
105  double weeks() const { return static_cast<double>(nanos) / WEEKS; }
106 
107  struct timeval timeval() const
108  {
109  struct timeval t;
110 
111  // Explicitly compute `tv_sec` and `tv_usec` instead of using `us` and
112  // `secs` to avoid converting `int64_t` -> `double` -> `long`.
113  t.tv_sec = static_cast<decltype(t.tv_sec)>(ns() / SECONDS);
114  t.tv_usec = static_cast<decltype(t.tv_usec)>(
115  (ns() / MICROSECONDS) - (t.tv_sec * SECONDS / MICROSECONDS));
116  return t;
117  }
118 
119  bool operator<(const Duration& d) const { return nanos < d.nanos; }
120  bool operator<=(const Duration& d) const { return nanos <= d.nanos; }
121  bool operator>(const Duration& d) const { return nanos > d.nanos; }
122  bool operator>=(const Duration& d) const { return nanos >= d.nanos; }
123  bool operator==(const Duration& d) const { return nanos == d.nanos; }
124  bool operator!=(const Duration& d) const { return nanos != d.nanos; }
125 
127  {
128  nanos += that.nanos;
129  return *this;
130  }
131 
133  {
134  nanos -= that.nanos;
135  return *this;
136  }
137 
138  template <typename T>
139  Duration& operator*=(T multiplier)
140  {
141  nanos = static_cast<int64_t>(nanos * multiplier);
142  return *this;
143  }
144 
145  template <typename T>
146  Duration& operator/=(T divisor)
147  {
148  nanos = static_cast<int64_t>(nanos / divisor);
149  return *this;
150  }
151 
152  Duration operator+(const Duration& that) const
153  {
154  Duration sum = *this;
155  sum += that;
156  return sum;
157  }
158 
159  Duration operator-(const Duration& that) const
160  {
161  Duration diff = *this;
162  diff -= that;
163  return diff;
164  }
165 
166  template <typename T>
167  Duration operator*(T multiplier) const
168  {
169  Duration product = *this;
170  product *= multiplier;
171  return product;
172  }
173 
174  template <typename T>
175  Duration operator/(T divisor) const
176  {
177  Duration quotient = *this;
178  quotient /= divisor;
179  return quotient;
180  }
181 
182  // A constant holding the maximum value a Duration can have.
183  static constexpr Duration max();
184  // A constant holding the minimum (negative) value a Duration can
185  // have.
186  static constexpr Duration min();
187  // A constant holding a Duration of a "zero" value.
188  static constexpr Duration zero() { return Duration(); }
189 
190 protected:
191  static constexpr int64_t NANOSECONDS = 1;
192  static constexpr int64_t MICROSECONDS = 1000 * NANOSECONDS;
193  static constexpr int64_t MILLISECONDS = 1000 * MICROSECONDS;
194  static constexpr int64_t SECONDS = 1000 * MILLISECONDS;
195  static constexpr int64_t MINUTES = 60 * SECONDS;
196  static constexpr int64_t HOURS = 60 * MINUTES;
197  static constexpr int64_t DAYS = 24 * HOURS;
198  static constexpr int64_t WEEKS = 7 * DAYS;
199 
200  // Construct from a (value, unit) pair.
201  constexpr Duration(int64_t value, int64_t unit)
202  : nanos(value * unit) {}
203 
204 private:
205  // Used only by "parse".
206  constexpr Duration(double value, int64_t unit)
207  : nanos(static_cast<int64_t>(value * unit)) {}
208 
209  int64_t nanos;
210 
211  friend std::ostream& operator<<(
212  std::ostream& stream,
213  const Duration& duration);
214 };
215 
216 
217 class Nanoseconds : public Duration
218 {
219 public:
220  explicit constexpr Nanoseconds(int64_t nanoseconds)
221  : Duration(nanoseconds, NANOSECONDS) {}
222 
223  constexpr Nanoseconds(const Duration& d) : Duration(d) {}
224 
225  double value() const { return static_cast<double>(this->ns()); }
226 
227  static std::string units() { return "ns"; }
228 };
229 
230 
231 class Microseconds : public Duration
232 {
233 public:
234  explicit constexpr Microseconds(int64_t microseconds)
235  : Duration(microseconds, MICROSECONDS) {}
236 
237  constexpr Microseconds(const Duration& d) : Duration(d) {}
238 
239  double value() const { return this->us(); }
240 
241  static std::string units() { return "us"; }
242 };
243 
244 
245 class Milliseconds : public Duration
246 {
247 public:
248  explicit constexpr Milliseconds(int64_t milliseconds)
249  : Duration(milliseconds, MILLISECONDS) {}
250 
251  constexpr Milliseconds(const Duration& d) : Duration(d) {}
252 
253  double value() const { return this->ms(); }
254 
255  static std::string units() { return "ms"; }
256 };
257 
258 
259 class Seconds : public Duration
260 {
261 public:
262  explicit constexpr Seconds(int64_t seconds)
263  : Duration(seconds, SECONDS) {}
264 
265  constexpr Seconds(const Duration& d) : Duration(d) {}
266 
267  double value() const { return this->secs(); }
268 
269  static std::string units() { return "secs"; }
270 };
271 
272 
273 class Minutes : public Duration
274 {
275 public:
276  explicit constexpr Minutes(int64_t minutes)
277  : Duration(minutes, MINUTES) {}
278 
279  constexpr Minutes(const Duration& d) : Duration(d) {}
280 
281  double value() const { return this->mins(); }
282 
283  static std::string units() { return "mins"; }
284 };
285 
286 
287 class Hours : public Duration
288 {
289 public:
290  explicit constexpr Hours(int64_t hours)
291  : Duration(hours, HOURS) {}
292 
293  constexpr Hours(const Duration& d) : Duration(d) {}
294 
295  double value() const { return this->hrs(); }
296 
297  static std::string units() { return "hrs"; }
298 };
299 
300 
301 class Days : public Duration
302 {
303 public:
304  explicit constexpr Days(int64_t days)
305  : Duration(days, DAYS) {}
306 
307  constexpr Days(const Duration& d) : Duration(d) {}
308 
309  double value() const { return this->days(); }
310 
311  static std::string units() { return "days"; }
312 };
313 
314 
315 class Weeks : public Duration
316 {
317 public:
318  explicit constexpr Weeks(int64_t value) : Duration(value, WEEKS) {}
319 
320  constexpr Weeks(const Duration& d) : Duration(d) {}
321 
322  double value() const { return this->weeks(); }
323 
324  static std::string units() { return "weeks"; }
325 };
326 
327 
328 inline std::ostream& operator<<(std::ostream& stream, const Duration& duration_)
329 {
330  // Output the duration in full double precision and save the old precision.
331  std::streamsize precision =
332  stream.precision(std::numeric_limits<double>::digits10);
333 
334  // Parse the duration as the sign and the absolute value.
335  Duration duration = duration_;
336  if (duration_ < Duration::zero()) {
337  stream << "-";
338 
339  // Duration::min() may not be representable as a positive Duration.
340  if (duration_ == Duration::min()) {
341  duration = Duration::max();
342  } else {
343  duration = duration_ * -1;
344  }
345  }
346 
347  // First determine which bucket of time unit the duration falls into
348  // then check whether the duration can be represented as a whole
349  // number with this time unit or a smaller one.
350  // e.g. 1.42857142857143weeks falls into the 'Weeks' bucket but
351  // reads better with a smaller unit: '10days'. So we use 'days'
352  // instead of 'weeks' to output the duration.
353  int64_t nanoseconds = duration.ns();
354  if (duration < Microseconds(1)) {
355  stream << duration.ns() << Nanoseconds::units();
356  } else if (duration < Milliseconds(1)) {
357  if (nanoseconds % Duration::MICROSECONDS != 0) {
358  // We can't get a whole number using this unit but we can at
359  // one level down.
360  stream << duration.ns() << Nanoseconds::units();
361  } else {
362  stream << duration.us() << Microseconds::units();
363  }
364  } else if (duration < Seconds(1)) {
365  if (nanoseconds % Duration::MILLISECONDS != 0 &&
366  nanoseconds % Duration::MICROSECONDS == 0) {
367  stream << duration.us() << Microseconds::units();
368  } else {
369  stream << duration.ms() << Milliseconds::units();
370  }
371  } else if (duration < Minutes(1)) {
372  if (nanoseconds % Duration::SECONDS != 0 &&
373  nanoseconds % Duration::MILLISECONDS == 0) {
374  stream << duration.ms() << Milliseconds::units();
375  } else {
376  stream << duration.secs() << Seconds::units();
377  }
378  } else if (duration < Hours(1)) {
379  if (nanoseconds % Duration::MINUTES != 0 &&
380  nanoseconds % Duration::SECONDS == 0) {
381  stream << duration.secs() << Seconds::units();
382  } else {
383  stream << duration.mins() << Minutes::units();
384  }
385  } else if (duration < Days(1)) {
386  if (nanoseconds % Duration::HOURS != 0 &&
387  nanoseconds % Duration::MINUTES == 0) {
388  stream << duration.mins() << Minutes::units();
389  } else {
390  stream << duration.hrs() << Hours::units();
391  }
392  } else if (duration < Weeks(1)) {
393  if (nanoseconds % Duration::DAYS != 0 &&
394  nanoseconds % Duration::HOURS == 0) {
395  stream << duration.hrs() << Hours::units();
396  } else {
397  stream << duration.days() << Days::units();
398  }
399  } else {
400  if (nanoseconds % Duration::WEEKS != 0 &&
401  nanoseconds % Duration::DAYS == 0) {
402  stream << duration.days() << Days::units();
403  } else {
404  stream << duration.weeks() << Weeks::units();
405  }
406  }
407 
408  // Return the stream to original formatting state.
409  stream.precision(precision);
410 
411  return stream;
412 }
413 
414 
415 inline Try<Duration> Duration::create(double seconds)
416 {
417  if (seconds * SECONDS > max().nanos || seconds * SECONDS < min().nanos) {
418  return Error("Argument out of the range that a Duration can represent due "
419  "to int64_t's size limit");
420  }
421 
422  return Nanoseconds(static_cast<int64_t>(seconds * SECONDS));
423 }
424 
425 
426 inline constexpr Duration Duration::max()
427 {
429 }
430 
431 
432 inline constexpr Duration Duration::min()
433 {
435 }
436 
437 #endif // __STOUT_DURATION_HPP__
double value() const
Definition: duration.hpp:309
double weeks() const
Definition: duration.hpp:105
Duration operator/(T divisor) const
Definition: duration.hpp:175
Try< Diff > diff(const std::string &from, const std::string &to)
Definition: svn.hpp:72
static std::string units()
Definition: duration.hpp:241
double value() const
Definition: duration.hpp:253
Definition: errorbase.hpp:35
static constexpr int64_t MICROSECONDS
Definition: duration.hpp:192
double value() const
Definition: duration.hpp:225
Duration & operator-=(const Duration &that)
Definition: duration.hpp:132
bool operator!=(const Duration &d) const
Definition: duration.hpp:124
double value() const
Definition: duration.hpp:295
Definition: try.hpp:34
bool operator>=(const Duration &d) const
Definition: duration.hpp:122
static std::string units()
Definition: duration.hpp:283
Duration operator*(T multiplier) const
Definition: duration.hpp:167
Duration operator+(const Duration &that) const
Definition: duration.hpp:152
double value() const
Definition: duration.hpp:281
constexpr Hours(const Duration &d)
Definition: duration.hpp:293
bool operator>(const Duration &d) const
Definition: duration.hpp:121
constexpr Duration(int64_t value, int64_t unit)
Definition: duration.hpp:201
static constexpr int64_t HOURS
Definition: duration.hpp:196
static constexpr int64_t MINUTES
Definition: duration.hpp:195
static constexpr int64_t SECONDS
Definition: duration.hpp:194
constexpr Microseconds(const Duration &d)
Definition: duration.hpp:237
constexpr Days(int64_t days)
Definition: duration.hpp:304
static Try< Duration > create(double seconds)
Definition: duration.hpp:415
double ms() const
Definition: duration.hpp:100
constexpr Weeks(int64_t value)
Definition: duration.hpp:318
bool operator==(const Duration &d) const
Definition: duration.hpp:123
Definition: duration.hpp:32
Duration(const timeval &t)
Definition: duration.hpp:93
constexpr Milliseconds(const Duration &d)
Definition: duration.hpp:251
double mins() const
Definition: duration.hpp:102
Duration operator-(const Duration &that) const
Definition: duration.hpp:159
static constexpr int64_t DAYS
Definition: duration.hpp:197
static std::string units()
Definition: duration.hpp:297
double days() const
Definition: duration.hpp:104
bool operator<=(const Duration &d) const
Definition: duration.hpp:120
static std::string units()
Definition: duration.hpp:311
Definition: duration.hpp:245
Duration & operator*=(T multiplier)
Definition: duration.hpp:139
constexpr Minutes(int64_t minutes)
Definition: duration.hpp:276
Definition: duration.hpp:273
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:199
Definition: duration.hpp:259
static constexpr int64_t MILLISECONDS
Definition: duration.hpp:193
constexpr Days(const Duration &d)
Definition: duration.hpp:307
constexpr Minutes(const Duration &d)
Definition: duration.hpp:279
int64_t ns() const
Definition: duration.hpp:98
Option< T > min(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:170
constexpr Weeks(const Duration &d)
Definition: duration.hpp:320
double value() const
Definition: duration.hpp:267
Duration & operator+=(const Duration &that)
Definition: duration.hpp:126
Definition: duration.hpp:301
double secs() const
Definition: duration.hpp:101
bool operator<(const Duration &d) const
Definition: duration.hpp:119
static Try error(const E &e)
Definition: try.hpp:42
static constexpr int64_t NANOSECONDS
Definition: duration.hpp:191
Definition: duration.hpp:287
Duration & operator/=(T divisor)
Definition: duration.hpp:146
double hrs() const
Definition: duration.hpp:103
constexpr Seconds(const Duration &d)
Definition: duration.hpp:265
Definition: duration.hpp:231
bool isError() const
Definition: try.hpp:71
static std::string units()
Definition: duration.hpp:269
static constexpr Duration zero()
Definition: duration.hpp:188
std::ostream & operator<<(std::ostream &stream, const Call::Type &type)
Definition: agent.hpp:28
double value() const
Definition: duration.hpp:239
constexpr Hours(int64_t hours)
Definition: duration.hpp:290
double us() const
Definition: duration.hpp:99
struct timeval timeval() const
Definition: duration.hpp:107
friend std::ostream & operator<<(std::ostream &stream, const Duration &duration)
Definition: duration.hpp:328
constexpr Nanoseconds(const Duration &d)
Definition: duration.hpp:223
static std::string units()
Definition: duration.hpp:324
Definition: duration.hpp:217
double value() const
Definition: duration.hpp:322
static constexpr int64_t WEEKS
Definition: duration.hpp:198
constexpr Duration()
Definition: duration.hpp:91
constexpr Milliseconds(int64_t milliseconds)
Definition: duration.hpp:248
static Try< Duration > parse(const std::string &s)
Definition: duration.hpp:35
Definition: duration.hpp:315
const T & get() const
Definition: try.hpp:73
static constexpr Duration min()
Definition: duration.hpp:432
static std::string units()
Definition: duration.hpp:227
static constexpr Duration max()
Definition: duration.hpp:426
static std::string units()
Definition: duration.hpp:255
constexpr Seconds(int64_t seconds)
Definition: duration.hpp:262
constexpr Microseconds(int64_t microseconds)
Definition: duration.hpp:234
constexpr Nanoseconds(int64_t nanoseconds)
Definition: duration.hpp:220