• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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