• 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 <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/transport/timeout_encoding.h"
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "src/core/lib/gpr/string.h"
27 
round_up(int64_t x,int64_t divisor)28 static int64_t round_up(int64_t x, int64_t divisor) {
29   return (x / divisor + (x % divisor != 0)) * divisor;
30 }
31 
32 /* round an integer up to the next value with three significant figures */
round_up_to_three_sig_figs(int64_t x)33 static int64_t round_up_to_three_sig_figs(int64_t x) {
34   if (x < 1000) return x;
35   if (x < 10000) return round_up(x, 10);
36   if (x < 100000) return round_up(x, 100);
37   if (x < 1000000) return round_up(x, 1000);
38   if (x < 10000000) return round_up(x, 10000);
39   if (x < 100000000) return round_up(x, 100000);
40   if (x < 1000000000) return round_up(x, 1000000);
41   return round_up(x, 10000000);
42 }
43 
44 /* encode our minimum viable timeout value */
enc_tiny(char * buffer)45 static void enc_tiny(char* buffer) { memcpy(buffer, "1n", 3); }
46 
enc_ext(char * buffer,int64_t value,char ext)47 static void enc_ext(char* buffer, int64_t value, char ext) {
48   int n = int64_ttoa(value, buffer);
49   buffer[n] = ext;
50   buffer[n + 1] = 0;
51 }
52 
enc_seconds(char * buffer,int64_t sec)53 static void enc_seconds(char* buffer, int64_t sec) {
54   if (sec % 3600 == 0) {
55     enc_ext(buffer, sec / 3600, 'H');
56   } else if (sec % 60 == 0) {
57     enc_ext(buffer, sec / 60, 'M');
58   } else {
59     enc_ext(buffer, sec, 'S');
60   }
61 }
62 
enc_millis(char * buffer,int64_t x)63 static void enc_millis(char* buffer, int64_t x) {
64   x = round_up_to_three_sig_figs(x);
65   if (x < GPR_MS_PER_SEC) {
66     enc_ext(buffer, x, 'm');
67   } else {
68     if (x % GPR_MS_PER_SEC == 0) {
69       enc_seconds(buffer, x / GPR_MS_PER_SEC);
70     } else {
71       enc_ext(buffer, x, 'm');
72     }
73   }
74 }
75 
grpc_http2_encode_timeout(grpc_millis timeout,char * buffer)76 void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer) {
77   if (timeout <= 0) {
78     enc_tiny(buffer);
79   } else if (timeout < 1000 * GPR_MS_PER_SEC) {
80     enc_millis(buffer, timeout);
81   } else {
82     enc_seconds(buffer,
83                 timeout / GPR_MS_PER_SEC + (timeout % GPR_MS_PER_SEC != 0));
84   }
85 }
86 
is_all_whitespace(const char * p,const char * end)87 static int is_all_whitespace(const char* p, const char* end) {
88   while (p != end && *p == ' ') p++;
89   return p == end;
90 }
91 
grpc_http2_decode_timeout(grpc_slice text,grpc_millis * timeout)92 int grpc_http2_decode_timeout(grpc_slice text, grpc_millis* timeout) {
93   grpc_millis x = 0;
94   const uint8_t* p = GRPC_SLICE_START_PTR(text);
95   const uint8_t* end = GRPC_SLICE_END_PTR(text);
96   int have_digit = 0;
97   /* skip whitespace */
98   for (; p != end && *p == ' '; p++)
99     ;
100   /* decode numeric part */
101   for (; p != end && *p >= '0' && *p <= '9'; p++) {
102     int32_t digit = static_cast<int32_t>(*p - static_cast<uint8_t>('0'));
103     have_digit = 1;
104     /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */
105     if (x >= (100 * 1000 * 1000)) {
106       if (x != (100 * 1000 * 1000) || digit != 0) {
107         *timeout = GRPC_MILLIS_INF_FUTURE;
108         return 1;
109       }
110     }
111     x = x * 10 + digit;
112   }
113   if (!have_digit) return 0;
114   /* skip whitespace */
115   for (; p != end && *p == ' '; p++)
116     ;
117   if (p == end) return 0;
118   /* decode unit specifier */
119   switch (*p) {
120     case 'n':
121       *timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0);
122       break;
123     case 'u':
124       *timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0);
125       break;
126     case 'm':
127       *timeout = x;
128       break;
129     case 'S':
130       *timeout = x * GPR_MS_PER_SEC;
131       break;
132     case 'M':
133       *timeout = x * 60 * GPR_MS_PER_SEC;
134       break;
135     case 'H':
136       *timeout = x * 60 * 60 * GPR_MS_PER_SEC;
137       break;
138     default:
139       return 0;
140   }
141   p++;
142   return is_all_whitespace(reinterpret_cast<const char*>(p),
143                            reinterpret_cast<const char*>(end));
144 }
145