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/ext/transport/chttp2/transport/bin_encoder.h"
22
23 #include <stdint.h>
24 #include <string.h>
25
26 #include <grpc/support/log.h>
27
28 #include "src/core/ext/transport/chttp2/transport/huffsyms.h"
29
30 static const char alphabet[] =
31 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
32
33 struct b64_huff_sym {
34 uint16_t bits;
35 uint8_t length;
36 };
37 static const b64_huff_sym huff_alphabet[64] = {
38 {0x21, 6}, {0x5d, 7}, {0x5e, 7}, {0x5f, 7}, {0x60, 7}, {0x61, 7},
39 {0x62, 7}, {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, {0x67, 7},
40 {0x68, 7}, {0x69, 7}, {0x6a, 7}, {0x6b, 7}, {0x6c, 7}, {0x6d, 7},
41 {0x6e, 7}, {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, {0xfc, 8},
42 {0x73, 7}, {0xfd, 8}, {0x3, 5}, {0x23, 6}, {0x4, 5}, {0x24, 6},
43 {0x5, 5}, {0x25, 6}, {0x26, 6}, {0x27, 6}, {0x6, 5}, {0x74, 7},
44 {0x75, 7}, {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, {0x2b, 6},
45 {0x76, 7}, {0x2c, 6}, {0x8, 5}, {0x9, 5}, {0x2d, 6}, {0x77, 7},
46 {0x78, 7}, {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x0, 5}, {0x1, 5},
47 {0x2, 5}, {0x19, 6}, {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6},
48 {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}};
49
50 static const uint8_t tail_xtra[3] = {0, 2, 3};
51
grpc_chttp2_base64_encode(const grpc_slice & input)52 grpc_slice grpc_chttp2_base64_encode(const grpc_slice& input) {
53 size_t input_length = GRPC_SLICE_LENGTH(input);
54 size_t input_triplets = input_length / 3;
55 size_t tail_case = input_length % 3;
56 size_t output_length = input_triplets * 4 + tail_xtra[tail_case];
57 grpc_slice output = GRPC_SLICE_MALLOC(output_length);
58 const uint8_t* in = GRPC_SLICE_START_PTR(input);
59 char* out = reinterpret_cast<char*> GRPC_SLICE_START_PTR(output);
60 size_t i;
61
62 // encode full triplets
63 for (i = 0; i < input_triplets; i++) {
64 out[0] = alphabet[in[0] >> 2];
65 out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)];
66 out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)];
67 out[3] = alphabet[in[2] & 0x3f];
68 out += 4;
69 in += 3;
70 }
71
72 // encode the remaining bytes
73 switch (tail_case) {
74 case 0:
75 break;
76 case 1:
77 out[0] = alphabet[in[0] >> 2];
78 out[1] = alphabet[(in[0] & 0x3) << 4];
79 out += 2;
80 in += 1;
81 break;
82 case 2:
83 out[0] = alphabet[in[0] >> 2];
84 out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)];
85 out[2] = alphabet[(in[1] & 0xf) << 2];
86 out += 3;
87 in += 2;
88 break;
89 }
90
91 GPR_ASSERT(out == (char*)GRPC_SLICE_END_PTR(output));
92 GPR_ASSERT(in == GRPC_SLICE_END_PTR(input));
93 return output;
94 }
95
grpc_chttp2_huffman_compress(const grpc_slice & input)96 grpc_slice grpc_chttp2_huffman_compress(const grpc_slice& input) {
97 size_t nbits;
98 const uint8_t* in;
99 uint8_t* out;
100 grpc_slice output;
101 uint64_t temp = 0;
102 uint32_t temp_length = 0;
103
104 nbits = 0;
105 for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input);
106 ++in) {
107 nbits += grpc_chttp2_huffsyms[*in].length;
108 }
109
110 output = GRPC_SLICE_MALLOC(nbits / 8 + (nbits % 8 != 0));
111 out = GRPC_SLICE_START_PTR(output);
112 for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input);
113 ++in) {
114 int sym = *in;
115 temp <<= grpc_chttp2_huffsyms[sym].length;
116 temp |= grpc_chttp2_huffsyms[sym].bits;
117 temp_length += grpc_chttp2_huffsyms[sym].length;
118
119 while (temp_length > 8) {
120 temp_length -= 8;
121 *out++ = static_cast<uint8_t>(temp >> temp_length);
122 }
123 }
124
125 if (temp_length) {
126 // NB: the following integer arithmetic operation needs to be in its
127 // expanded form due to the "integral promotion" performed (see section
128 // 3.2.1.1 of the C89 draft standard). A cast to the smaller container type
129 // is then required to avoid the compiler warning
130 *out++ =
131 static_cast<uint8_t>(static_cast<uint8_t>(temp << (8u - temp_length)) |
132 static_cast<uint8_t>(0xffu >> temp_length));
133 }
134
135 GPR_ASSERT(out == GRPC_SLICE_END_PTR(output));
136
137 return output;
138 }
139
140 struct huff_out {
141 uint32_t temp;
142 uint32_t temp_length;
143 uint8_t* out;
144 };
enc_flush_some(huff_out * out)145 static void enc_flush_some(huff_out* out) {
146 while (out->temp_length > 8) {
147 out->temp_length -= 8;
148 *out->out++ = static_cast<uint8_t>(out->temp >> out->temp_length);
149 }
150 }
151
enc_add2(huff_out * out,uint8_t a,uint8_t b,uint32_t * wire_size)152 static void enc_add2(huff_out* out, uint8_t a, uint8_t b, uint32_t* wire_size) {
153 *wire_size += 2;
154 b64_huff_sym sa = huff_alphabet[a];
155 b64_huff_sym sb = huff_alphabet[b];
156 out->temp = (out->temp << (sa.length + sb.length)) |
157 (static_cast<uint32_t>(sa.bits) << sb.length) | sb.bits;
158 out->temp_length +=
159 static_cast<uint32_t>(sa.length) + static_cast<uint32_t>(sb.length);
160 enc_flush_some(out);
161 }
162
enc_add1(huff_out * out,uint8_t a,uint32_t * wire_size)163 static void enc_add1(huff_out* out, uint8_t a, uint32_t* wire_size) {
164 *wire_size += 1;
165 b64_huff_sym sa = huff_alphabet[a];
166 out->temp = (out->temp << sa.length) | sa.bits;
167 out->temp_length += sa.length;
168 enc_flush_some(out);
169 }
170
grpc_chttp2_base64_encode_and_huffman_compress(const grpc_slice & input,uint32_t * wire_size)171 grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(
172 const grpc_slice& input, uint32_t* wire_size) {
173 size_t input_length = GRPC_SLICE_LENGTH(input);
174 size_t input_triplets = input_length / 3;
175 size_t tail_case = input_length % 3;
176 size_t output_syms = input_triplets * 4 + tail_xtra[tail_case];
177 size_t max_output_bits = 11 * output_syms;
178 size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0);
179 grpc_slice output = GRPC_SLICE_MALLOC(max_output_length);
180 const uint8_t* in = GRPC_SLICE_START_PTR(input);
181 uint8_t* start_out = GRPC_SLICE_START_PTR(output);
182 huff_out out;
183 size_t i;
184
185 out.temp = 0;
186 out.temp_length = 0;
187 out.out = start_out;
188 *wire_size = 0;
189
190 // encode full triplets
191 for (i = 0; i < input_triplets; i++) {
192 const uint8_t low_to_high = static_cast<uint8_t>((in[0] & 0x3) << 4);
193 const uint8_t high_to_low = in[1] >> 4;
194 enc_add2(&out, in[0] >> 2, low_to_high | high_to_low, wire_size);
195
196 const uint8_t a = static_cast<uint8_t>((in[1] & 0xf) << 2);
197 const uint8_t b = (in[2] >> 6);
198 enc_add2(&out, a | b, in[2] & 0x3f, wire_size);
199 in += 3;
200 }
201
202 // encode the remaining bytes
203 switch (tail_case) {
204 case 0:
205 break;
206 case 1:
207 enc_add2(&out, in[0] >> 2, static_cast<uint8_t>((in[0] & 0x3) << 4),
208 wire_size);
209 in += 1;
210 break;
211 case 2: {
212 const uint8_t low_to_high = static_cast<uint8_t>((in[0] & 0x3) << 4);
213 const uint8_t high_to_low = in[1] >> 4;
214 enc_add2(&out, in[0] >> 2, low_to_high | high_to_low, wire_size);
215 enc_add1(&out, static_cast<uint8_t>((in[1] & 0xf) << 2), wire_size);
216 in += 2;
217 break;
218 }
219 }
220
221 if (out.temp_length) {
222 // NB: the following integer arithmetic operation needs to be in its
223 // expanded form due to the "integral promotion" performed (see section
224 // 3.2.1.1 of the C89 draft standard). A cast to the smaller container type
225 // is then required to avoid the compiler warning
226 *out.out++ = static_cast<uint8_t>(
227 static_cast<uint8_t>(out.temp << (8u - out.temp_length)) |
228 static_cast<uint8_t>(0xffu >> out.temp_length));
229 }
230
231 GPR_ASSERT(out.out <= GRPC_SLICE_END_PTR(output));
232 GRPC_SLICE_SET_LENGTH(output, out.out - start_out);
233
234 GPR_ASSERT(in == GRPC_SLICE_END_PTR(input));
235 return output;
236 }
237