ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
ROS 2 C++ Client Library with ROS Client Library
duration.cpp
1 // Copyright 2017 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cmath>
16 #include <cstdlib>
17 #include <limits>
18 #include <utility>
19 
20 #include "rclcpp/clock.hpp"
21 #include "rclcpp/time.hpp"
22 
23 #include "builtin_interfaces/msg/duration.hpp"
24 
25 #include "rcl/time.h"
26 
27 #include "rclcpp/exceptions.hpp"
28 
29 #include "rcutils/logging_macros.h"
30 
31 #include "rclcpp/utilities.hpp"
32 
33 namespace rclcpp
34 {
35 
36 Duration::Duration(int32_t seconds, uint32_t nanoseconds)
37 {
38  rcl_duration_.nanoseconds = RCL_S_TO_NS(static_cast<int64_t>(seconds));
39  rcl_duration_.nanoseconds += nanoseconds;
40 }
41 
42 Duration::Duration(std::chrono::nanoseconds nanoseconds)
43 {
44  rcl_duration_.nanoseconds = nanoseconds.count();
45 }
46 
47 Duration::Duration(const Duration & rhs) = default;
48 
49 Duration::Duration(
50  const builtin_interfaces::msg::Duration & duration_msg)
51 {
52  rcl_duration_.nanoseconds =
53  RCL_S_TO_NS(static_cast<rcl_duration_value_t>(duration_msg.sec));
54  rcl_duration_.nanoseconds += static_cast<rcl_duration_value_t>(duration_msg.nanosec);
55 }
56 
57 Duration::Duration(const rcl_duration_t & duration)
58 : rcl_duration_(duration)
59 {
60  // noop
61 }
62 
63 Duration::operator builtin_interfaces::msg::Duration() const
64 {
65  builtin_interfaces::msg::Duration msg_duration;
66  constexpr rcl_duration_value_t kDivisor = RCL_S_TO_NS(1);
67  constexpr int32_t max_s = std::numeric_limits<int32_t>::max();
68  constexpr int32_t min_s = std::numeric_limits<int32_t>::min();
69  constexpr uint32_t max_ns = std::numeric_limits<uint32_t>::max();
70  const auto result = std::div(rcl_duration_.nanoseconds, kDivisor);
71  if (result.rem >= 0) {
72  // saturate if we will overflow
73  if (result.quot > max_s) {
74  msg_duration.sec = max_s;
75  msg_duration.nanosec = max_ns;
76  } else {
77  msg_duration.sec = static_cast<int32_t>(result.quot);
78  msg_duration.nanosec = static_cast<uint32_t>(result.rem);
79  }
80  } else {
81  if (result.quot <= min_s) {
82  msg_duration.sec = min_s;
83  msg_duration.nanosec = 0u;
84  } else {
85  msg_duration.sec = static_cast<int32_t>(result.quot - 1);
86  msg_duration.nanosec = static_cast<uint32_t>(kDivisor + result.rem);
87  }
88  }
89  return msg_duration;
90 }
91 
92 Duration &
93 Duration::operator=(const Duration & rhs) = default;
94 
95 Duration &
96 Duration::operator=(const builtin_interfaces::msg::Duration & duration_msg)
97 {
98  *this = Duration(duration_msg);
99  return *this;
100 }
101 
102 bool
103 Duration::operator==(const rclcpp::Duration & rhs) const
104 {
105  return rcl_duration_.nanoseconds == rhs.rcl_duration_.nanoseconds;
106 }
107 
108 bool
109 Duration::operator!=(const rclcpp::Duration & rhs) const
110 {
111  return rcl_duration_.nanoseconds != rhs.rcl_duration_.nanoseconds;
112 }
113 
114 bool
115 Duration::operator<(const rclcpp::Duration & rhs) const
116 {
117  return rcl_duration_.nanoseconds < rhs.rcl_duration_.nanoseconds;
118 }
119 
120 bool
121 Duration::operator<=(const rclcpp::Duration & rhs) const
122 {
123  return rcl_duration_.nanoseconds <= rhs.rcl_duration_.nanoseconds;
124 }
125 
126 bool
127 Duration::operator>=(const rclcpp::Duration & rhs) const
128 {
129  return rcl_duration_.nanoseconds >= rhs.rcl_duration_.nanoseconds;
130 }
131 
132 bool
133 Duration::operator>(const rclcpp::Duration & rhs) const
134 {
135  return rcl_duration_.nanoseconds > rhs.rcl_duration_.nanoseconds;
136 }
137 
138 void
139 bounds_check_duration_sum(int64_t lhsns, int64_t rhsns, uint64_t max)
140 {
141  auto abs_lhs = static_cast<uint64_t>(std::abs(lhsns));
142  auto abs_rhs = static_cast<uint64_t>(std::abs(rhsns));
143 
144  if (lhsns > 0 && rhsns > 0) {
145  if (abs_lhs + abs_rhs > max) {
146  throw std::overflow_error("addition leads to int64_t overflow");
147  }
148  } else if (lhsns < 0 && rhsns < 0) {
149  if (abs_lhs + abs_rhs > max) {
150  throw std::underflow_error("addition leads to int64_t underflow");
151  }
152  }
153 }
154 
155 Duration
156 Duration::operator+(const rclcpp::Duration & rhs) const
157 {
158  bounds_check_duration_sum(
159  this->rcl_duration_.nanoseconds,
160  rhs.rcl_duration_.nanoseconds,
161  std::numeric_limits<rcl_duration_value_t>::max());
163  rcl_duration_.nanoseconds + rhs.rcl_duration_.nanoseconds);
164 }
165 
166 Duration &
167 Duration::operator+=(const rclcpp::Duration & rhs)
168 {
169  *this = *this + rhs;
170  return *this;
171 }
172 
173 void
174 bounds_check_duration_difference(int64_t lhsns, int64_t rhsns, uint64_t max)
175 {
176  auto abs_lhs = static_cast<uint64_t>(std::abs(lhsns));
177  auto abs_rhs = static_cast<uint64_t>(std::abs(rhsns));
178 
179  if (lhsns > 0 && rhsns < 0) {
180  if (abs_lhs + abs_rhs > max) {
181  throw std::overflow_error("duration subtraction leads to int64_t overflow");
182  }
183  } else if (lhsns < 0 && rhsns > 0) {
184  if (abs_lhs + abs_rhs > max) {
185  throw std::underflow_error("duration subtraction leads to int64_t underflow");
186  }
187  }
188 }
189 
190 Duration
191 Duration::operator-(const rclcpp::Duration & rhs) const
192 {
193  bounds_check_duration_difference(
194  this->rcl_duration_.nanoseconds,
195  rhs.rcl_duration_.nanoseconds,
196  std::numeric_limits<rcl_duration_value_t>::max());
197 
199  rcl_duration_.nanoseconds - rhs.rcl_duration_.nanoseconds);
200 }
201 
202 Duration &
203 Duration::operator-=(const rclcpp::Duration & rhs)
204 {
205  *this = *this - rhs;
206  return *this;
207 }
208 
209 void
210 bounds_check_duration_scale(int64_t dns, double scale, uint64_t max)
211 {
212  auto abs_dns = static_cast<uint64_t>(std::abs(dns));
213  auto abs_scale = std::abs(scale);
214  if (abs_scale > 1.0 && abs_dns >
215  static_cast<uint64_t>(static_cast<long double>(max) / static_cast<long double>(abs_scale)))
216  {
217  if ((dns > 0 && scale > 0) || (dns < 0 && scale < 0)) {
218  throw std::overflow_error("duration scaling leads to int64_t overflow");
219  } else {
220  throw std::underflow_error("duration scaling leads to int64_t underflow");
221  }
222  }
223 }
224 
225 Duration
226 Duration::operator*(double scale) const
227 {
228  if (!std::isfinite(scale)) {
229  throw std::runtime_error("abnormal scale in rclcpp::Duration");
230  }
231  bounds_check_duration_scale(
232  this->rcl_duration_.nanoseconds,
233  scale,
234  std::numeric_limits<rcl_duration_value_t>::max());
235  long double scale_ld = static_cast<long double>(scale);
237  static_cast<rcl_duration_value_t>(
238  static_cast<long double>(rcl_duration_.nanoseconds) * scale_ld));
239 }
240 
241 Duration &
242 Duration::operator*=(double scale)
243 {
244  *this = *this * scale;
245  return *this;
246 }
247 
250 {
251  return rcl_duration_.nanoseconds;
252 }
253 
254 Duration
256 {
257  return Duration(std::numeric_limits<int32_t>::max(), 999999999);
258 }
259 
260 double
262 {
263  return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_duration_.nanoseconds)).count();
264 }
265 
266 rmw_time_t
268 {
269  if (rcl_duration_.nanoseconds < 0) {
270  throw std::runtime_error("rmw_time_t cannot be negative");
271  }
272 
273  // Purposefully avoid creating from builtin_interfaces::msg::Duration
274  // to avoid possible overflow converting from int64_t to int32_t, then back to uint64_t
275  rmw_time_t result;
276  constexpr rcl_duration_value_t kDivisor = RCL_S_TO_NS(1);
277  const auto div_result = std::div(rcl_duration_.nanoseconds, kDivisor);
278  result.sec = static_cast<uint64_t>(div_result.quot);
279  result.nsec = static_cast<uint64_t>(div_result.rem);
280 
281  return result;
282 }
283 
284 Duration
285 Duration::from_rmw_time(rmw_time_t duration)
286 {
287  Duration ret;
288  constexpr rcl_duration_value_t limit_ns = std::numeric_limits<rcl_duration_value_t>::max();
289  constexpr rcl_duration_value_t limit_sec = RCL_NS_TO_S(limit_ns);
290  if (duration.sec > limit_sec || duration.nsec > limit_ns) {
291  // saturate if will overflow
292  ret.rcl_duration_.nanoseconds = limit_ns;
293  return ret;
294  }
295  uint64_t total_ns = RCL_S_TO_NS(duration.sec) + duration.nsec;
296  if (total_ns > limit_ns) {
297  // saturate if will overflow
298  ret.rcl_duration_.nanoseconds = limit_ns;
299  return ret;
300  }
301  ret.rcl_duration_.nanoseconds = static_cast<rcl_duration_value_t>(total_ns);
302  return ret;
303 }
304 
305 Duration
306 Duration::from_seconds(double seconds)
307 {
308  Duration ret;
309  ret.rcl_duration_.nanoseconds = static_cast<int64_t>(RCL_S_TO_NS(seconds));
310  return ret;
311 }
312 
313 Duration
315 {
316  Duration ret;
317  ret.rcl_duration_.nanoseconds = nanoseconds;
318  return ret;
319 }
320 
321 builtin_interfaces::msg::Time
322 operator+(const builtin_interfaces::msg::Time & lhs, const rclcpp::Duration & rhs)
323 {
324  if (lhs.sec < 0) {
325  throw std::runtime_error("message time is negative");
326  }
327 
328  rcl_time_point_value_t rcl_time;
329  rcl_time = RCL_S_TO_NS(static_cast<int64_t>(lhs.sec));
330  rcl_time += lhs.nanosec;
331 
332  if (rclcpp::add_will_overflow(rcl_time, rhs.nanoseconds())) {
333  throw std::overflow_error("addition leads to int64_t overflow");
334  }
335  if (rclcpp::add_will_underflow(rcl_time, rhs.nanoseconds())) {
336  throw std::underflow_error("addition leads to int64_t underflow");
337  }
338 
339  rcl_time += rhs.nanoseconds();
340 
341  return convert_rcl_time_to_sec_nanos(rcl_time);
342 }
343 
344 builtin_interfaces::msg::Time
345 operator-(const builtin_interfaces::msg::Time & lhs, const rclcpp::Duration & rhs)
346 {
347  if (lhs.sec < 0) {
348  throw std::runtime_error("message time is negative");
349  }
350 
351  rcl_time_point_value_t rcl_time;
352  rcl_time = RCL_S_TO_NS(static_cast<int64_t>(lhs.sec));
353  rcl_time += lhs.nanosec;
354 
355  if (rclcpp::sub_will_overflow(rcl_time, rhs.nanoseconds())) {
356  throw std::overflow_error("addition leads to int64_t overflow");
357  }
358  if (rclcpp::sub_will_underflow(rcl_time, rhs.nanoseconds())) {
359  throw std::underflow_error("addition leads to int64_t underflow");
360  }
361 
362  rcl_time -= rhs.nanoseconds();
363 
364  return convert_rcl_time_to_sec_nanos(rcl_time);
365 }
366 
367 } // namespace rclcpp
rcl_duration_value_t nanoseconds() const
Get duration in nanosecods.
Definition: duration.cpp:249
double seconds() const
Get duration in seconds.
Definition: duration.cpp:261
static Duration from_nanoseconds(rcl_duration_value_t nanoseconds)
Create a duration object from an integer number representing nanoseconds.
Definition: duration.cpp:314
static Duration max()
Get the maximum representable value.
Definition: duration.cpp:255
rmw_time_t to_rmw_time() const
Convert Duration into rmw_time_t.
Definition: duration.cpp:267
static Duration from_seconds(double seconds)
Create a duration object from a floating point number representing seconds.
Definition: duration.cpp:306
Duration(int32_t seconds, uint32_t nanoseconds)
Duration constructor.
Definition: duration.cpp:36
Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
RCLCPP_PUBLIC builtin_interfaces::msg::Time convert_rcl_time_to_sec_nanos(const rcl_time_point_value_t &time_point)
Convert rcl_time_point_value_t to builtin_interfaces::msg::Time.
Definition: time.cpp:288
bool add_will_underflow(const T x, const T y)
Safely check if addition will underflow.
Definition: utilities.hpp:265
bool sub_will_overflow(const T x, const T y)
Safely check if subtraction will overflow.
Definition: utilities.hpp:282
bool add_will_overflow(const T x, const T y)
Safely check if addition will overflow.
Definition: utilities.hpp:248
bool sub_will_underflow(const T x, const T y)
Safely check if subtraction will underflow.
Definition: utilities.hpp:299
A duration of time, measured in nanoseconds and its source.
Definition: time.h:75
rcl_duration_value_t nanoseconds
Duration in nanoseconds and its source.
Definition: time.h:77
#define RCL_NS_TO_S
Convenience macro to convert nanoseconds to seconds.
Definition: time.h:39
rcutils_time_point_value_t rcl_time_point_value_t
A single point in time, measured in nanoseconds since the Unix epoch.
Definition: time.h:46
rcutils_duration_value_t rcl_duration_value_t
A duration of time, measured in nanoseconds.
Definition: time.h:48
#define RCL_S_TO_NS
Convenience macro to convert seconds to nanoseconds.
Definition: time.h:32