• 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/compression/compression_internal.h"
22 
23 #include <stdlib.h>
24 
25 #include <string>
26 
27 #include "absl/container/inlined_vector.h"
28 #include "absl/strings/ascii.h"
29 #include "absl/strings/str_format.h"
30 #include "absl/strings/str_split.h"
31 
32 #include <grpc/compression.h>
33 #include <grpc/support/log.h>
34 
35 #include "src/core/lib/channel/channel_args.h"
36 #include "src/core/lib/debug/trace.h"
37 #include "src/core/lib/gprpp/crash.h"
38 #include "src/core/lib/gprpp/ref_counted_ptr.h"
39 #include "src/core/lib/gprpp/ref_counted_string.h"
40 
41 namespace grpc_core {
42 
CompressionAlgorithmAsString(grpc_compression_algorithm algorithm)43 const char* CompressionAlgorithmAsString(grpc_compression_algorithm algorithm) {
44   switch (algorithm) {
45     case GRPC_COMPRESS_NONE:
46       return "identity";
47     case GRPC_COMPRESS_DEFLATE:
48       return "deflate";
49     case GRPC_COMPRESS_GZIP:
50       return "gzip";
51     case GRPC_COMPRESS_ALGORITHMS_COUNT:
52     default:
53       return nullptr;
54   }
55 }
56 
57 namespace {
58 class CommaSeparatedLists {
59  public:
CommaSeparatedLists()60   CommaSeparatedLists() : lists_{}, text_buffer_{} {
61     char* text_buffer = text_buffer_;
__anona375bbf10202(char c) 62     auto add_char = [&text_buffer, this](char c) {
63       if (text_buffer - text_buffer_ == kTextBufferSize) abort();
64       *text_buffer++ = c;
65     };
66     for (size_t list = 0; list < kNumLists; ++list) {
67       char* start = text_buffer;
68       for (size_t algorithm = 0; algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT;
69            ++algorithm) {
70         if ((list & (1 << algorithm)) == 0) continue;
71         if (start != text_buffer) {
72           add_char(',');
73           add_char(' ');
74         }
75         const char* name = CompressionAlgorithmAsString(
76             static_cast<grpc_compression_algorithm>(algorithm));
77         for (const char* p = name; *p != '\0'; ++p) {
78           add_char(*p);
79         }
80       }
81       lists_[list] = absl::string_view(start, text_buffer - start);
82     }
83     if (text_buffer - text_buffer_ != kTextBufferSize) abort();
84   }
85 
operator [](size_t list) const86   absl::string_view operator[](size_t list) const { return lists_[list]; }
87 
88  private:
89   static constexpr size_t kNumLists = 1 << GRPC_COMPRESS_ALGORITHMS_COUNT;
90   // Experimentally determined (tweak things until it runs).
91   static constexpr size_t kTextBufferSize = 86;
92   absl::string_view lists_[kNumLists];
93   char text_buffer_[kTextBufferSize];
94 };
95 
96 const CommaSeparatedLists kCommaSeparatedLists;
97 }  // namespace
98 
ParseCompressionAlgorithm(absl::string_view algorithm)99 absl::optional<grpc_compression_algorithm> ParseCompressionAlgorithm(
100     absl::string_view algorithm) {
101   if (algorithm == "identity") {
102     return GRPC_COMPRESS_NONE;
103   } else if (algorithm == "deflate") {
104     return GRPC_COMPRESS_DEFLATE;
105   } else if (algorithm == "gzip") {
106     return GRPC_COMPRESS_GZIP;
107   } else {
108     return absl::nullopt;
109   }
110 }
111 
112 grpc_compression_algorithm
CompressionAlgorithmForLevel(grpc_compression_level level) const113 CompressionAlgorithmSet::CompressionAlgorithmForLevel(
114     grpc_compression_level level) const {
115   if (level > GRPC_COMPRESS_LEVEL_HIGH) {
116     Crash(absl::StrFormat("Unknown message compression level %d.",
117                           static_cast<int>(level)));
118   }
119 
120   if (level == GRPC_COMPRESS_LEVEL_NONE) {
121     return GRPC_COMPRESS_NONE;
122   }
123 
124   GPR_ASSERT(level > 0);
125 
126   // Establish a "ranking" or compression algorithms in increasing order of
127   // compression.
128   // This is simplistic and we will probably want to introduce other dimensions
129   // in the future (cpu/memory cost, etc).
130   absl::InlinedVector<grpc_compression_algorithm,
131                       GRPC_COMPRESS_ALGORITHMS_COUNT>
132       algos;
133   for (auto algo : {GRPC_COMPRESS_GZIP, GRPC_COMPRESS_DEFLATE}) {
134     if (set_.is_set(algo)) {
135       algos.push_back(algo);
136     }
137   }
138 
139   if (algos.empty()) {
140     return GRPC_COMPRESS_NONE;
141   }
142 
143   switch (level) {
144     case GRPC_COMPRESS_LEVEL_NONE:
145       abort();  // should have been handled already
146     case GRPC_COMPRESS_LEVEL_LOW:
147       return algos[0];
148     case GRPC_COMPRESS_LEVEL_MED:
149       return algos[algos.size() / 2];
150     case GRPC_COMPRESS_LEVEL_HIGH:
151       return algos.back();
152     default:
153       abort();
154   };
155 }
156 
FromUint32(uint32_t value)157 CompressionAlgorithmSet CompressionAlgorithmSet::FromUint32(uint32_t value) {
158   CompressionAlgorithmSet set;
159   for (size_t i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
160     if (value & (1u << i)) {
161       set.set_.set(i);
162     }
163   }
164   return set;
165 }
166 
FromChannelArgs(const ChannelArgs & args)167 CompressionAlgorithmSet CompressionAlgorithmSet::FromChannelArgs(
168     const ChannelArgs& args) {
169   CompressionAlgorithmSet set;
170   static const uint32_t kEverything =
171       (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1;
172   return CompressionAlgorithmSet::FromUint32(
173       args.GetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)
174           .value_or(kEverything));
175 }
176 
177 CompressionAlgorithmSet::CompressionAlgorithmSet() = default;
178 
CompressionAlgorithmSet(std::initializer_list<grpc_compression_algorithm> algorithms)179 CompressionAlgorithmSet::CompressionAlgorithmSet(
180     std::initializer_list<grpc_compression_algorithm> algorithms) {
181   for (auto algorithm : algorithms) {
182     Set(algorithm);
183   }
184 }
185 
IsSet(grpc_compression_algorithm algorithm) const186 bool CompressionAlgorithmSet::IsSet(
187     grpc_compression_algorithm algorithm) const {
188   size_t i = static_cast<size_t>(algorithm);
189   if (i < GRPC_COMPRESS_ALGORITHMS_COUNT) {
190     return set_.is_set(i);
191   } else {
192     return false;
193   }
194 }
195 
Set(grpc_compression_algorithm algorithm)196 void CompressionAlgorithmSet::Set(grpc_compression_algorithm algorithm) {
197   size_t i = static_cast<size_t>(algorithm);
198   if (i < GRPC_COMPRESS_ALGORITHMS_COUNT) {
199     set_.set(i);
200   }
201 }
202 
ToString() const203 absl::string_view CompressionAlgorithmSet::ToString() const {
204   return kCommaSeparatedLists[ToLegacyBitmask()];
205 }
206 
ToSlice() const207 Slice CompressionAlgorithmSet::ToSlice() const {
208   return Slice::FromStaticString(ToString());
209 }
210 
FromString(absl::string_view str)211 CompressionAlgorithmSet CompressionAlgorithmSet::FromString(
212     absl::string_view str) {
213   CompressionAlgorithmSet set{GRPC_COMPRESS_NONE};
214   for (auto algorithm : absl::StrSplit(str, ',')) {
215     auto parsed =
216         ParseCompressionAlgorithm(absl::StripAsciiWhitespace(algorithm));
217     if (parsed.has_value()) {
218       set.Set(*parsed);
219     }
220   }
221   return set;
222 }
223 
ToLegacyBitmask() const224 uint32_t CompressionAlgorithmSet::ToLegacyBitmask() const {
225   return set_.ToInt<uint32_t>();
226 }
227 
228 absl::optional<grpc_compression_algorithm>
DefaultCompressionAlgorithmFromChannelArgs(const ChannelArgs & args)229 DefaultCompressionAlgorithmFromChannelArgs(const ChannelArgs& args) {
230   auto* value = args.Get(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM);
231   if (value == nullptr) return absl::nullopt;
232   auto ival = value->GetIfInt();
233   if (ival.has_value()) {
234     return static_cast<grpc_compression_algorithm>(*ival);
235   }
236   auto sval = value->GetIfString();
237   if (sval != nullptr) {
238     return ParseCompressionAlgorithm(sval->as_string_view());
239   }
240   return absl::nullopt;
241 }
242 
CompressionOptionsFromChannelArgs(const ChannelArgs & args)243 grpc_compression_options CompressionOptionsFromChannelArgs(
244     const ChannelArgs& args) {
245   // Set compression options.
246   grpc_compression_options compression_options;
247   grpc_compression_options_init(&compression_options);
248   auto default_level = args.GetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL);
249   if (default_level.has_value()) {
250     compression_options.default_level.is_set = true;
251     compression_options.default_level.level = Clamp(
252         static_cast<grpc_compression_level>(*default_level),
253         GRPC_COMPRESS_LEVEL_NONE,
254         static_cast<grpc_compression_level>(GRPC_COMPRESS_LEVEL_COUNT - 1));
255   }
256   auto default_algorithm =
257       args.GetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM);
258   if (default_algorithm.has_value()) {
259     compression_options.default_algorithm.is_set = true;
260     compression_options.default_algorithm.algorithm =
261         Clamp(static_cast<grpc_compression_algorithm>(*default_algorithm),
262               GRPC_COMPRESS_NONE,
263               static_cast<grpc_compression_algorithm>(
264                   GRPC_COMPRESS_ALGORITHMS_COUNT - 1));
265   }
266   auto enabled_algorithms_bitset =
267       args.GetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET);
268   if (enabled_algorithms_bitset.has_value()) {
269     compression_options.enabled_algorithms_bitset =
270         *enabled_algorithms_bitset | 1 /* always support no compression */;
271   }
272   return compression_options;
273 }
274 
275 }  // namespace grpc_core
276