Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include "src/core/transport/chttp2/timeout_encoding.h"
35 :
36 : #include <stdio.h>
37 : #include <string.h>
38 :
39 : #include "src/core/support/string.h"
40 :
41 5273 : static int round_up(int x, int divisor) {
42 5273 : return (x / divisor + (x % divisor != 0)) * divisor;
43 : }
44 :
45 : /* round an integer up to the next value with three significant figures */
46 5274 : static int round_up_to_three_sig_figs(int x) {
47 5274 : if (x < 1000) return x;
48 5273 : if (x < 10000) return round_up(x, 10);
49 5272 : if (x < 100000) return round_up(x, 100);
50 5271 : if (x < 1000000) return round_up(x, 1000);
51 5267 : if (x < 10000000) return round_up(x, 10000);
52 331 : if (x < 100000000) return round_up(x, 100000);
53 82 : if (x < 1000000000) return round_up(x, 1000000);
54 0 : return round_up(x, 10000000);
55 : }
56 :
57 : /* encode our minimum viable timeout value */
58 2 : static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); }
59 :
60 5296 : static void enc_ext(char *buffer, long value, char ext) {
61 5296 : int n = gpr_ltoa(value, buffer);
62 5296 : buffer[n] = ext;
63 5296 : buffer[n + 1] = 0;
64 5296 : }
65 :
66 24 : static void enc_seconds(char *buffer, long sec) {
67 24 : if (sec % 3600 == 0) {
68 14 : enc_ext(buffer, sec / 3600, 'H');
69 10 : } else if (sec % 60 == 0) {
70 3 : enc_ext(buffer, sec / 60, 'M');
71 : } else {
72 7 : enc_ext(buffer, sec, 'S');
73 : }
74 24 : }
75 :
76 55 : static void enc_nanos(char *buffer, int x) {
77 55 : x = round_up_to_three_sig_figs(x);
78 55 : if (x < 100000) {
79 3 : if (x % 1000 == 0) {
80 2 : enc_ext(buffer, x / 1000, 'u');
81 : } else {
82 1 : enc_ext(buffer, x, 'n');
83 : }
84 52 : } else if (x < 100000000) {
85 50 : if (x % 1000000 == 0) {
86 2 : enc_ext(buffer, x / 1000000, 'm');
87 : } else {
88 48 : enc_ext(buffer, x / 1000, 'u');
89 : }
90 2 : } else if (x < 1000000000) {
91 0 : enc_ext(buffer, x / 1000000, 'm');
92 : } else {
93 : /* note that this is only ever called with times of less than one second,
94 : so if we reach here the time must have been rounded up to a whole second
95 : (and no more) */
96 2 : memcpy(buffer, "1S", 3);
97 : }
98 55 : }
99 :
100 5219 : static void enc_micros(char *buffer, int x) {
101 5219 : x = round_up_to_three_sig_figs(x);
102 5219 : if (x < 100000) {
103 0 : if (x % 1000 == 0) {
104 0 : enc_ext(buffer, x / 1000, 'm');
105 : } else {
106 0 : enc_ext(buffer, x, 'u');
107 : }
108 5219 : } else if (x < 100000000) {
109 5127 : if (x % 1000000 == 0) {
110 4585 : enc_ext(buffer, x / 1000000, 'S');
111 : } else {
112 542 : enc_ext(buffer, x / 1000, 'm');
113 : }
114 : } else {
115 92 : enc_ext(buffer, x / 1000000, 'S');
116 : }
117 5219 : }
118 :
119 5300 : void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) {
120 5300 : if (timeout.tv_sec < 0) {
121 2 : enc_tiny(buffer);
122 5298 : } else if (timeout.tv_sec == 0) {
123 55 : enc_nanos(buffer, timeout.tv_nsec);
124 5243 : } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) {
125 5219 : enc_micros(buffer,
126 5219 : (int)(timeout.tv_sec * 1000000) +
127 5219 : (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0)));
128 : } else {
129 24 : enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0));
130 : }
131 5300 : }
132 :
133 3492 : static int is_all_whitespace(const char *p) {
134 3492 : while (*p == ' ') p++;
135 3492 : return *p == 0;
136 : }
137 :
138 3501 : int grpc_chttp2_decode_timeout(const char *buffer, gpr_timespec *timeout) {
139 3501 : gpr_uint32 x = 0;
140 3501 : const gpr_uint8 *p = (const gpr_uint8 *)buffer;
141 3501 : int have_digit = 0;
142 : /* skip whitespace */
143 3501 : for (; *p == ' '; p++)
144 : ;
145 : /* decode numeric part */
146 10593 : for (; *p >= '0' && *p <= '9'; p++) {
147 7093 : gpr_uint32 xp = x * 10u + (gpr_uint32)*p - (gpr_uint32)'0';
148 7093 : have_digit = 1;
149 7093 : if (xp < x) {
150 1 : *timeout = gpr_inf_future(GPR_CLOCK_REALTIME);
151 1 : return 1;
152 : }
153 7092 : x = xp;
154 : }
155 3500 : if (!have_digit) return 0;
156 : /* skip whitespace */
157 3494 : for (; *p == ' '; p++)
158 : ;
159 : /* decode unit specifier */
160 3494 : switch (*p) {
161 : case 'n':
162 68 : *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN);
163 68 : break;
164 : case 'u':
165 112 : *timeout = gpr_time_from_micros(x, GPR_TIMESPAN);
166 112 : break;
167 : case 'm':
168 577 : *timeout = gpr_time_from_millis(x, GPR_TIMESPAN);
169 577 : break;
170 : case 'S':
171 2587 : *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN);
172 2587 : break;
173 : case 'M':
174 68 : *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN);
175 68 : break;
176 : case 'H':
177 80 : *timeout = gpr_time_from_hours(x, GPR_TIMESPAN);
178 80 : break;
179 : default:
180 2 : return 0;
181 : }
182 3492 : p++;
183 3492 : return is_all_whitespace((const char *)p);
184 : }
|