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