1 //
2 //
3 // Copyright 2015 gRPC authors.
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
19 #include "src/core/lib/transport/timeout_encoding.h"
20
21 #include <grpc/support/port_platform.h>
22 #include <grpc/support/time.h>
23
24 #include <limits>
25
26 #include "absl/base/attributes.h"
27 #include "absl/log/check.h"
28
29 namespace grpc_core {
30
31 namespace {
32
DivideRoundingUp(int64_t dividend,int64_t divisor)33 int64_t DivideRoundingUp(int64_t dividend, int64_t divisor) {
34 return (dividend - 1 + divisor) / divisor;
35 }
36
37 constexpr int64_t kSecondsPerMinute = 60;
38 constexpr int64_t kMinutesPerHour = 60;
39 constexpr int64_t kMaxHours = 27000;
40
IsAllSpace(const uint8_t * p,const uint8_t * end)41 bool IsAllSpace(const uint8_t* p, const uint8_t* end) {
42 while (p != end && *p == ' ') p++;
43 return p == end;
44 }
45
46 } // namespace
47
FromDuration(Duration duration)48 Timeout Timeout::FromDuration(Duration duration) {
49 return Timeout::FromMillis(duration.millis());
50 }
51
RatioVersus(Timeout other) const52 double Timeout::RatioVersus(Timeout other) const {
53 double a = AsDuration().millis();
54 double b = other.AsDuration().millis();
55 if (b == 0) {
56 if (a > 0) return 100;
57 if (a < 0) return -100;
58 return 0;
59 }
60 return 100 * (a / b - 1);
61 }
62
AsDuration() const63 Duration Timeout::AsDuration() const {
64 int64_t value = value_;
65 switch (unit_) {
66 case Unit::kNanoseconds:
67 return Duration::Zero();
68 case Unit::kMilliseconds:
69 return Duration::Milliseconds(value);
70 case Unit::kTenMilliseconds:
71 return Duration::Milliseconds(value * 10);
72 case Unit::kHundredMilliseconds:
73 return Duration::Milliseconds(value * 100);
74 case Unit::kSeconds:
75 return Duration::Seconds(value);
76 case Unit::kTenSeconds:
77 return Duration::Seconds(value * 10);
78 case Unit::kHundredSeconds:
79 return Duration::Seconds(value * 100);
80 case Unit::kMinutes:
81 return Duration::Minutes(value);
82 case Unit::kTenMinutes:
83 return Duration::Minutes(value * 10);
84 case Unit::kHundredMinutes:
85 return Duration::Minutes(value * 100);
86 case Unit::kHours:
87 return Duration::Hours(value);
88 }
89 GPR_UNREACHABLE_CODE(return Duration::NegativeInfinity());
90 }
91
Encode() const92 Slice Timeout::Encode() const {
93 char buf[10];
94 char* p = buf;
95 uint16_t n = value_;
96 int digits;
97 if (n >= 10000) {
98 digits = 5;
99 } else if (n >= 1000) {
100 digits = 4;
101 } else if (n >= 100) {
102 digits = 3;
103 } else if (n >= 10) {
104 digits = 2;
105 } else {
106 digits = 1;
107 }
108 switch (digits) {
109 case 5:
110 *p++ = '0' + n / 10000;
111 n %= 10000;
112 ABSL_FALLTHROUGH_INTENDED;
113 case 4:
114 *p++ = '0' + n / 1000;
115 n %= 1000;
116 ABSL_FALLTHROUGH_INTENDED;
117 case 3:
118 *p++ = '0' + n / 100;
119 n %= 100;
120 ABSL_FALLTHROUGH_INTENDED;
121 case 2:
122 *p++ = '0' + n / 10;
123 n %= 10;
124 ABSL_FALLTHROUGH_INTENDED;
125 case 1:
126 *p++ = '0' + n;
127 }
128 switch (unit_) {
129 case Unit::kNanoseconds:
130 *p++ = 'n';
131 break;
132 case Unit::kHundredMilliseconds:
133 *p++ = '0';
134 ABSL_FALLTHROUGH_INTENDED;
135 case Unit::kTenMilliseconds:
136 *p++ = '0';
137 ABSL_FALLTHROUGH_INTENDED;
138 case Unit::kMilliseconds:
139 *p++ = 'm';
140 break;
141 case Unit::kHundredSeconds:
142 *p++ = '0';
143 ABSL_FALLTHROUGH_INTENDED;
144 case Unit::kTenSeconds:
145 *p++ = '0';
146 ABSL_FALLTHROUGH_INTENDED;
147 case Unit::kSeconds:
148 *p++ = 'S';
149 break;
150 case Unit::kHundredMinutes:
151 *p++ = '0';
152 ABSL_FALLTHROUGH_INTENDED;
153 case Unit::kTenMinutes:
154 *p++ = '0';
155 ABSL_FALLTHROUGH_INTENDED;
156 case Unit::kMinutes:
157 *p++ = 'M';
158 break;
159 case Unit::kHours:
160 *p++ = 'H';
161 break;
162 }
163 return Slice::FromCopiedBuffer(buf, p - buf);
164 }
165
FromMillis(int64_t millis)166 Timeout Timeout::FromMillis(int64_t millis) {
167 if (millis <= 0) {
168 return Timeout(1, Unit::kNanoseconds);
169 } else if (millis < 1000) {
170 return Timeout(millis, Unit::kMilliseconds);
171 } else if (millis < 10000) {
172 int64_t value = DivideRoundingUp(millis, 10);
173 if (value % 100 != 0) return Timeout(value, Unit::kTenMilliseconds);
174 } else if (millis < 100000) {
175 int64_t value = DivideRoundingUp(millis, 100);
176 if (value % 10 != 0) return Timeout(value, Unit::kHundredMilliseconds);
177 } else if (millis > std::numeric_limits<int64_t>::max() - 999) {
178 // prevent signed integer overflow.
179 return Timeout(kMaxHours, Unit::kHours);
180 }
181 return Timeout::FromSeconds(DivideRoundingUp(millis, 1000));
182 }
183
FromSeconds(int64_t seconds)184 Timeout Timeout::FromSeconds(int64_t seconds) {
185 DCHECK_NE(seconds, 0);
186 if (seconds < 1000) {
187 if (seconds % kSecondsPerMinute != 0) {
188 return Timeout(seconds, Unit::kSeconds);
189 }
190 } else if (seconds < 10000) {
191 int64_t value = DivideRoundingUp(seconds, 10);
192 if ((value * 10) % kSecondsPerMinute != 0) {
193 return Timeout(value, Unit::kTenSeconds);
194 }
195 } else if (seconds < 100000) {
196 int64_t value = DivideRoundingUp(seconds, 100);
197 if ((value * 100) % kSecondsPerMinute != 0) {
198 return Timeout(value, Unit::kHundredSeconds);
199 }
200 }
201 return Timeout::FromMinutes(DivideRoundingUp(seconds, kSecondsPerMinute));
202 }
203
FromMinutes(int64_t minutes)204 Timeout Timeout::FromMinutes(int64_t minutes) {
205 DCHECK_NE(minutes, 0);
206 if (minutes < 1000) {
207 if (minutes % kMinutesPerHour != 0) {
208 return Timeout(minutes, Unit::kMinutes);
209 }
210 } else if (minutes < 10000) {
211 int64_t value = DivideRoundingUp(minutes, 10);
212 if ((value * 10) % kMinutesPerHour != 0) {
213 return Timeout(value, Unit::kTenMinutes);
214 }
215 } else if (minutes < 100000) {
216 int64_t value = DivideRoundingUp(minutes, 100);
217 if ((value * 100) % kMinutesPerHour != 0) {
218 return Timeout(value, Unit::kHundredMinutes);
219 }
220 }
221 return Timeout::FromHours(DivideRoundingUp(minutes, kMinutesPerHour));
222 }
223
FromHours(int64_t hours)224 Timeout Timeout::FromHours(int64_t hours) {
225 DCHECK_NE(hours, 0);
226 if (hours < kMaxHours) {
227 return Timeout(hours, Unit::kHours);
228 }
229 return Timeout(kMaxHours, Unit::kHours);
230 }
231
ParseTimeout(const Slice & text)232 absl::optional<Duration> ParseTimeout(const Slice& text) {
233 int32_t x = 0;
234 const uint8_t* p = text.begin();
235 const uint8_t* end = text.end();
236 int have_digit = 0;
237 // skip whitespace
238 for (; p != end && *p == ' '; p++) {
239 }
240 // decode numeric part
241 for (; p != end && *p >= '0' && *p <= '9'; p++) {
242 int32_t digit = static_cast<int32_t>(*p - static_cast<uint8_t>('0'));
243 have_digit = 1;
244 // spec allows max. 8 digits, but we allow values up to 1,000,000,000
245 if (x >= (100 * 1000 * 1000)) {
246 if (x != (100 * 1000 * 1000) || digit != 0) {
247 return Duration::Infinity();
248 }
249 }
250 x = x * 10 + digit;
251 }
252 if (!have_digit) return absl::nullopt;
253 // skip whitespace
254 for (; p != end && *p == ' '; p++) {
255 }
256 if (p == end) return absl::nullopt;
257 // decode unit specifier
258 Duration timeout;
259 switch (*p) {
260 case 'n':
261 timeout = Duration::Milliseconds((x / GPR_NS_PER_MS) +
262 (x % GPR_NS_PER_MS != 0));
263 break;
264 case 'u':
265 timeout = Duration::Milliseconds((x / GPR_US_PER_MS) +
266 (x % GPR_US_PER_MS != 0));
267 break;
268 case 'm':
269 timeout = Duration::Milliseconds(x);
270 break;
271 case 'S':
272 timeout = Duration::Seconds(x);
273 break;
274 case 'M':
275 timeout = Duration::Minutes(x);
276 break;
277 case 'H':
278 timeout = Duration::Hours(x);
279 break;
280 default:
281 return absl::nullopt;
282 }
283 p++;
284 if (!IsAllSpace(p, end)) return absl::nullopt;
285 return timeout;
286 }
287
288 } // namespace grpc_core
289