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