• 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/channel/channel_args.h"
20 
21 #include <grpc/impl/channel_arg_names.h>
22 #include <grpc/support/alloc.h>
23 #include <grpc/support/port_platform.h>
24 #include <grpc/support/string_util.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <algorithm>
30 #include <map>
31 #include <memory>
32 #include <string>
33 #include <vector>
34 
35 #include "absl/log/check.h"
36 #include "absl/log/log.h"
37 #include "absl/strings/match.h"
38 #include "absl/strings/str_cat.h"
39 #include "absl/strings/str_format.h"
40 #include "absl/strings/str_join.h"
41 #include "src/core/util/useful.h"
42 
43 namespace grpc_core {
44 
45 const grpc_arg_pointer_vtable ChannelArgs::Value::int_vtable_{
46     // copy
__anon40c8e0f50102(void* p) 47     [](void* p) { return p; },
48     // destroy
__anon40c8e0f50202(void*) 49     [](void*) {},
50     // cmp
__anon40c8e0f50302(void* p1, void* p2) 51     [](void* p1, void* p2) -> int {
52       return QsortCompare(reinterpret_cast<intptr_t>(p1),
53                           reinterpret_cast<intptr_t>(p2));
54     },
55 };
56 
57 const grpc_arg_pointer_vtable ChannelArgs::Value::string_vtable_{
58     // copy
__anon40c8e0f50402(void* p) 59     [](void* p) -> void* {
60       return static_cast<RefCountedString*>(p)->Ref().release();
61     },
62     // destroy
__anon40c8e0f50502(void* p) 63     [](void* p) { static_cast<RefCountedString*>(p)->Unref(); },
64     // cmp
__anon40c8e0f50602(void* p1, void* p2) 65     [](void* p1, void* p2) -> int {
66       return QsortCompare(static_cast<RefCountedString*>(p1)->as_string_view(),
67                           static_cast<RefCountedString*>(p2)->as_string_view());
68     },
69 };
70 
Pointer(void * p,const grpc_arg_pointer_vtable * vtable)71 ChannelArgs::Pointer::Pointer(void* p, const grpc_arg_pointer_vtable* vtable)
72     : p_(p), vtable_(vtable == nullptr ? EmptyVTable() : vtable) {}
73 
Pointer(const Pointer & other)74 ChannelArgs::Pointer::Pointer(const Pointer& other)
75     : p_(other.vtable_->copy(other.p_)), vtable_(other.vtable_) {}
76 
Pointer(Pointer && other)77 ChannelArgs::Pointer::Pointer(Pointer&& other) noexcept
78     : p_(other.p_), vtable_(other.vtable_) {
79   other.p_ = nullptr;
80   other.vtable_ = EmptyVTable();
81 }
82 
EmptyVTable()83 const grpc_arg_pointer_vtable* ChannelArgs::Pointer::EmptyVTable() {
84   static const grpc_arg_pointer_vtable vtable = {
85       // copy
86       [](void* p) { return p; },
87       // destroy
88       [](void*) {},
89       // cmp
90       [](void* p1, void* p2) -> int { return QsortCompare(p1, p2); },
91   };
92   return &vtable;
93 }
94 
95 ChannelArgs::ChannelArgs() = default;
96 ChannelArgs::~ChannelArgs() = default;
97 ChannelArgs::ChannelArgs(const ChannelArgs& other) = default;
98 ChannelArgs& ChannelArgs::operator=(const ChannelArgs& other) = default;
99 ChannelArgs::ChannelArgs(ChannelArgs&& other) noexcept = default;
100 ChannelArgs& ChannelArgs::operator=(ChannelArgs&& other) noexcept = default;
101 
Get(absl::string_view name) const102 const ChannelArgs::Value* ChannelArgs::Get(absl::string_view name) const {
103   return args_.Lookup(name);
104 }
105 
Contains(absl::string_view name) const106 bool ChannelArgs::Contains(absl::string_view name) const {
107   return Get(name) != nullptr;
108 }
109 
operator <(const ChannelArgs & other) const110 bool ChannelArgs::operator<(const ChannelArgs& other) const {
111   return args_ < other.args_;
112 }
113 
operator ==(const ChannelArgs & other) const114 bool ChannelArgs::operator==(const ChannelArgs& other) const {
115   return args_ == other.args_;
116 }
117 
operator !=(const ChannelArgs & other) const118 bool ChannelArgs::operator!=(const ChannelArgs& other) const {
119   return !(*this == other);
120 }
121 
WantMinimalStack() const122 bool ChannelArgs::WantMinimalStack() const {
123   return GetBool(GRPC_ARG_MINIMAL_STACK).value_or(false);
124 }
125 
ChannelArgs(AVL<RefCountedStringValue,Value> args)126 ChannelArgs::ChannelArgs(AVL<RefCountedStringValue, Value> args)
127     : args_(std::move(args)) {}
128 
Set(grpc_arg arg) const129 ChannelArgs ChannelArgs::Set(grpc_arg arg) const {
130   switch (arg.type) {
131     case GRPC_ARG_INTEGER:
132       return Set(arg.key, arg.value.integer);
133     case GRPC_ARG_STRING:
134       if (arg.value.string != nullptr) return Set(arg.key, arg.value.string);
135       return Set(arg.key, "");
136     case GRPC_ARG_POINTER:
137       return Set(arg.key,
138                  Pointer(arg.value.pointer.vtable->copy(arg.value.pointer.p),
139                          arg.value.pointer.vtable));
140   }
141   GPR_UNREACHABLE_CODE(return ChannelArgs());
142 }
143 
FromC(const grpc_channel_args * args)144 ChannelArgs ChannelArgs::FromC(const grpc_channel_args* args) {
145   ChannelArgs result;
146   if (args != nullptr) {
147     for (size_t i = 0; i < args->num_args; i++) {
148       result = result.Set(args->args[i]);
149     }
150   }
151   return result;
152 }
153 
MakeCArg(const char * name) const154 grpc_arg ChannelArgs::Value::MakeCArg(const char* name) const {
155   char* c_name = const_cast<char*>(name);
156   if (rep_.c_vtable() == &int_vtable_) {
157     return grpc_channel_arg_integer_create(
158         c_name, reinterpret_cast<intptr_t>(rep_.c_pointer()));
159   }
160   if (rep_.c_vtable() == &string_vtable_) {
161     return grpc_channel_arg_string_create(
162         c_name, const_cast<char*>(
163                     static_cast<RefCountedString*>(rep_.c_pointer())->c_str()));
164   }
165   return grpc_channel_arg_pointer_create(c_name, rep_.c_pointer(),
166                                          rep_.c_vtable());
167 }
168 
ToC() const169 ChannelArgs::CPtr ChannelArgs::ToC() const {
170   std::vector<grpc_arg> c_args;
171   args_.ForEach(
172       [&c_args](const RefCountedStringValue& key, const Value& value) {
173         c_args.push_back(value.MakeCArg(key.c_str()));
174       });
175   return CPtr(static_cast<const grpc_channel_args*>(
176       grpc_channel_args_copy_and_add(nullptr, c_args.data(), c_args.size())));
177 }
178 
Set(absl::string_view name,Pointer value) const179 ChannelArgs ChannelArgs::Set(absl::string_view name, Pointer value) const {
180   return Set(name, Value(std::move(value)));
181 }
182 
Set(absl::string_view name,int value) const183 ChannelArgs ChannelArgs::Set(absl::string_view name, int value) const {
184   return Set(name, Value(value));
185 }
186 
Set(absl::string_view name,Value value) const187 ChannelArgs ChannelArgs::Set(absl::string_view name, Value value) const {
188   if (const auto* p = args_.Lookup(name)) {
189     if (*p == value) return *this;  // already have this value for this key
190   }
191   return ChannelArgs(args_.Add(RefCountedStringValue(name), std::move(value)));
192 }
193 
Set(absl::string_view name,absl::string_view value) const194 ChannelArgs ChannelArgs::Set(absl::string_view name,
195                              absl::string_view value) const {
196   return Set(name, std::string(value));
197 }
198 
Set(absl::string_view name,const char * value) const199 ChannelArgs ChannelArgs::Set(absl::string_view name, const char* value) const {
200   return Set(name, std::string(value));
201 }
202 
Set(absl::string_view name,std::string value) const203 ChannelArgs ChannelArgs::Set(absl::string_view name, std::string value) const {
204   return Set(name, Value(std::move(value)));
205 }
206 
Remove(absl::string_view name) const207 ChannelArgs ChannelArgs::Remove(absl::string_view name) const {
208   if (args_.Lookup(name) == nullptr) return *this;
209   return ChannelArgs(args_.Remove(name));
210 }
211 
RemoveAllKeysWithPrefix(absl::string_view prefix) const212 ChannelArgs ChannelArgs::RemoveAllKeysWithPrefix(
213     absl::string_view prefix) const {
214   auto args = args_;
215   args_.ForEach([&](const RefCountedStringValue& key, const Value&) {
216     if (absl::StartsWith(key.as_string_view(), prefix)) args = args.Remove(key);
217   });
218   return ChannelArgs(std::move(args));
219 }
220 
GetInt(absl::string_view name) const221 absl::optional<int> ChannelArgs::GetInt(absl::string_view name) const {
222   auto* v = Get(name);
223   if (v == nullptr) return absl::nullopt;
224   return v->GetIfInt();
225 }
226 
GetDurationFromIntMillis(absl::string_view name) const227 absl::optional<Duration> ChannelArgs::GetDurationFromIntMillis(
228     absl::string_view name) const {
229   auto ms = GetInt(name);
230   if (!ms.has_value()) return absl::nullopt;
231   if (*ms == INT_MAX) return Duration::Infinity();
232   if (*ms == INT_MIN) return Duration::NegativeInfinity();
233   return Duration::Milliseconds(*ms);
234 }
235 
GetString(absl::string_view name) const236 absl::optional<absl::string_view> ChannelArgs::GetString(
237     absl::string_view name) const {
238   auto* v = Get(name);
239   if (v == nullptr) return absl::nullopt;
240   const auto s = v->GetIfString();
241   if (s == nullptr) return absl::nullopt;
242   return s->as_string_view();
243 }
244 
GetOwnedString(absl::string_view name) const245 absl::optional<std::string> ChannelArgs::GetOwnedString(
246     absl::string_view name) const {
247   absl::optional<absl::string_view> v = GetString(name);
248   if (!v.has_value()) return absl::nullopt;
249   return std::string(*v);
250 }
251 
GetVoidPointer(absl::string_view name) const252 void* ChannelArgs::GetVoidPointer(absl::string_view name) const {
253   auto* v = Get(name);
254   if (v == nullptr) return nullptr;
255   const auto* pp = v->GetIfPointer();
256   if (pp == nullptr) return nullptr;
257   return pp->c_pointer();
258 }
259 
GetBool(absl::string_view name) const260 absl::optional<bool> ChannelArgs::GetBool(absl::string_view name) const {
261   auto* v = Get(name);
262   if (v == nullptr) return absl::nullopt;
263   auto i = v->GetIfInt();
264   if (!i.has_value()) {
265     LOG(ERROR) << name << " ignored: it must be an integer";
266     return absl::nullopt;
267   }
268   switch (*i) {
269     case 0:
270       return false;
271     case 1:
272       return true;
273     default:
274       LOG(ERROR) << name << " treated as bool but set to " << *i
275                  << " (assuming true)";
276       return true;
277   }
278 }
279 
ToString(std::list<std::string> & backing_strings) const280 absl::string_view ChannelArgs::Value::ToString(
281     std::list<std::string>& backing_strings) const {
282   if (rep_.c_vtable() == &string_vtable_) {
283     return static_cast<RefCountedString*>(rep_.c_pointer())->as_string_view();
284   }
285   if (rep_.c_vtable() == &int_vtable_) {
286     backing_strings.emplace_back(
287         std::to_string(reinterpret_cast<intptr_t>(rep_.c_pointer())));
288     return backing_strings.back();
289   }
290   backing_strings.emplace_back(absl::StrFormat("%p", rep_.c_pointer()));
291   return backing_strings.back();
292 }
293 
ToString() const294 std::string ChannelArgs::ToString() const {
295   std::vector<absl::string_view> strings;
296   std::list<std::string> backing_strings;
297   strings.push_back("{");
298   bool first = true;
299   args_.ForEach([&strings, &first, &backing_strings](
300                     const RefCountedStringValue& key, const Value& value) {
301     if (!first) strings.push_back(", ");
302     first = false;
303     strings.push_back(key.as_string_view());
304     strings.push_back("=");
305     strings.push_back(value.ToString(backing_strings));
306   });
307   strings.push_back("}");
308   return absl::StrJoin(strings, "");
309 }
310 
UnionWith(ChannelArgs other) const311 ChannelArgs ChannelArgs::UnionWith(ChannelArgs other) const {
312   if (args_.Empty()) return other;
313   if (other.args_.Empty()) return *this;
314   if (args_.Height() <= other.args_.Height()) {
315     args_.ForEach(
316         [&other](const RefCountedStringValue& key, const Value& value) {
317           other.args_ = other.args_.Add(key, value);
318         });
319     return other;
320   } else {
321     auto result = *this;
322     other.args_.ForEach(
323         [&result](const RefCountedStringValue& key, const Value& value) {
324           if (result.args_.Lookup(key) == nullptr) {
325             result.args_ = result.args_.Add(key, value);
326           }
327         });
328     return result;
329   }
330 }
331 
FuzzingReferenceUnionWith(ChannelArgs other) const332 ChannelArgs ChannelArgs::FuzzingReferenceUnionWith(ChannelArgs other) const {
333   // DO NOT OPTIMIZE THIS!!
334   args_.ForEach([&other](const RefCountedStringValue& key, const Value& value) {
335     other.args_ = other.args_.Add(key, value);
336   });
337   return other;
338 }
339 
operator ()(const grpc_channel_args * p) const340 void ChannelArgs::ChannelArgsDeleter::operator()(
341     const grpc_channel_args* p) const {
342   grpc_channel_args_destroy(p);
343 }
344 
operator <<(std::ostream & out,const ChannelArgs & args)345 std::ostream& operator<<(std::ostream& out, const ChannelArgs& args) {
346   return out << args.ToString();
347 }
348 
349 }  // namespace grpc_core
350 
copy_arg(const grpc_arg * src)351 static grpc_arg copy_arg(const grpc_arg* src) {
352   grpc_arg dst;
353   dst.type = src->type;
354   dst.key = gpr_strdup(src->key);
355   switch (dst.type) {
356     case GRPC_ARG_STRING:
357       dst.value.string = gpr_strdup(src->value.string);
358       break;
359     case GRPC_ARG_INTEGER:
360       dst.value.integer = src->value.integer;
361       break;
362     case GRPC_ARG_POINTER:
363       dst.value.pointer = src->value.pointer;
364       dst.value.pointer.p =
365           src->value.pointer.vtable->copy(src->value.pointer.p);
366       break;
367   }
368   return dst;
369 }
370 
grpc_channel_args_copy_and_add(const grpc_channel_args * src,const grpc_arg * to_add,size_t num_to_add)371 grpc_channel_args* grpc_channel_args_copy_and_add(const grpc_channel_args* src,
372                                                   const grpc_arg* to_add,
373                                                   size_t num_to_add) {
374   return grpc_channel_args_copy_and_add_and_remove(src, nullptr, 0, to_add,
375                                                    num_to_add);
376 }
377 
grpc_channel_args_copy_and_remove(const grpc_channel_args * src,const char ** to_remove,size_t num_to_remove)378 grpc_channel_args* grpc_channel_args_copy_and_remove(
379     const grpc_channel_args* src, const char** to_remove,
380     size_t num_to_remove) {
381   return grpc_channel_args_copy_and_add_and_remove(src, to_remove,
382                                                    num_to_remove, nullptr, 0);
383 }
384 
should_remove_arg(const grpc_arg * arg,const char ** to_remove,size_t num_to_remove)385 static bool should_remove_arg(const grpc_arg* arg, const char** to_remove,
386                               size_t num_to_remove) {
387   for (size_t i = 0; i < num_to_remove; ++i) {
388     if (strcmp(arg->key, to_remove[i]) == 0) return true;
389   }
390   return false;
391 }
392 
grpc_channel_args_copy_and_add_and_remove(const grpc_channel_args * src,const char ** to_remove,size_t num_to_remove,const grpc_arg * to_add,size_t num_to_add)393 grpc_channel_args* grpc_channel_args_copy_and_add_and_remove(
394     const grpc_channel_args* src, const char** to_remove, size_t num_to_remove,
395     const grpc_arg* to_add, size_t num_to_add) {
396   // Figure out how many args we'll be copying.
397   size_t num_args_to_copy = 0;
398   if (src != nullptr) {
399     for (size_t i = 0; i < src->num_args; ++i) {
400       if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) {
401         ++num_args_to_copy;
402       }
403     }
404   }
405   // Create result.
406   grpc_channel_args* dst =
407       static_cast<grpc_channel_args*>(gpr_malloc(sizeof(grpc_channel_args)));
408   dst->num_args = num_args_to_copy + num_to_add;
409   if (dst->num_args == 0) {
410     dst->args = nullptr;
411     return dst;
412   }
413   dst->args =
414       static_cast<grpc_arg*>(gpr_malloc(sizeof(grpc_arg) * dst->num_args));
415   // Copy args from src that are not being removed.
416   size_t dst_idx = 0;
417   if (src != nullptr) {
418     for (size_t i = 0; i < src->num_args; ++i) {
419       if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) {
420         dst->args[dst_idx++] = copy_arg(&src->args[i]);
421       }
422     }
423   }
424   // Add args from to_add.
425   for (size_t i = 0; i < num_to_add; ++i) {
426     dst->args[dst_idx++] = copy_arg(&to_add[i]);
427   }
428   CHECK(dst_idx == dst->num_args);
429   return dst;
430 }
431 
grpc_channel_args_copy(const grpc_channel_args * src)432 grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src) {
433   return grpc_channel_args_copy_and_add(src, nullptr, 0);
434 }
435 
grpc_channel_args_union(const grpc_channel_args * a,const grpc_channel_args * b)436 grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a,
437                                            const grpc_channel_args* b) {
438   if (a == nullptr) return grpc_channel_args_copy(b);
439   if (b == nullptr) return grpc_channel_args_copy(a);
440   const size_t max_out = (a->num_args + b->num_args);
441   grpc_arg* uniques =
442       static_cast<grpc_arg*>(gpr_malloc(sizeof(*uniques) * max_out));
443   for (size_t i = 0; i < a->num_args; ++i) uniques[i] = a->args[i];
444 
445   size_t uniques_idx = a->num_args;
446   for (size_t i = 0; i < b->num_args; ++i) {
447     const char* b_key = b->args[i].key;
448     if (grpc_channel_args_find(a, b_key) == nullptr) {  // not found
449       uniques[uniques_idx++] = b->args[i];
450     }
451   }
452   grpc_channel_args* result =
453       grpc_channel_args_copy_and_add(nullptr, uniques, uniques_idx);
454   gpr_free(uniques);
455   return result;
456 }
457 
cmp_arg(const grpc_arg * a,const grpc_arg * b)458 static int cmp_arg(const grpc_arg* a, const grpc_arg* b) {
459   int c = grpc_core::QsortCompare(a->type, b->type);
460   if (c != 0) return c;
461   c = strcmp(a->key, b->key);
462   if (c != 0) return c;
463   switch (a->type) {
464     case GRPC_ARG_STRING:
465       return strcmp(a->value.string, b->value.string);
466     case GRPC_ARG_INTEGER:
467       return grpc_core::QsortCompare(a->value.integer, b->value.integer);
468     case GRPC_ARG_POINTER:
469       return grpc_core::channel_args_detail::PointerCompare(
470           a->value.pointer.p, a->value.pointer.vtable, b->value.pointer.p,
471           b->value.pointer.vtable);
472   }
473   GPR_UNREACHABLE_CODE(return 0);
474 }
475 
476 // stabilizing comparison function: since channel_args ordering matters for
477 // keys with the same name, we need to preserve that ordering
cmp_key_stable(const void * ap,const void * bp)478 static int cmp_key_stable(const void* ap, const void* bp) {
479   const grpc_arg* const* a = static_cast<const grpc_arg* const*>(ap);
480   const grpc_arg* const* b = static_cast<const grpc_arg* const*>(bp);
481   int c = strcmp((*a)->key, (*b)->key);
482   if (c == 0) c = grpc_core::QsortCompare(*a, *b);
483   return c;
484 }
485 
grpc_channel_args_normalize(const grpc_channel_args * src)486 grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* src) {
487   grpc_arg** args =
488       static_cast<grpc_arg**>(gpr_malloc(sizeof(grpc_arg*) * src->num_args));
489   for (size_t i = 0; i < src->num_args; i++) {
490     args[i] = &src->args[i];
491   }
492   if (src->num_args > 1) {
493     qsort(args, src->num_args, sizeof(grpc_arg*), cmp_key_stable);
494   }
495 
496   grpc_channel_args* b =
497       static_cast<grpc_channel_args*>(gpr_malloc(sizeof(grpc_channel_args)));
498   b->num_args = src->num_args;
499   b->args = static_cast<grpc_arg*>(gpr_malloc(sizeof(grpc_arg) * b->num_args));
500   for (size_t i = 0; i < src->num_args; i++) {
501     b->args[i] = copy_arg(args[i]);
502   }
503 
504   gpr_free(args);
505   return b;
506 }
507 
grpc_channel_args_destroy(grpc_channel_args * a)508 void grpc_channel_args_destroy(grpc_channel_args* a) {
509   size_t i;
510   if (!a) return;
511   for (i = 0; i < a->num_args; i++) {
512     switch (a->args[i].type) {
513       case GRPC_ARG_STRING:
514         gpr_free(a->args[i].value.string);
515         break;
516       case GRPC_ARG_INTEGER:
517         break;
518       case GRPC_ARG_POINTER:
519         a->args[i].value.pointer.vtable->destroy(a->args[i].value.pointer.p);
520         break;
521     }
522     gpr_free(a->args[i].key);
523   }
524   gpr_free(a->args);
525   gpr_free(a);
526 }
527 
grpc_channel_args_compare(const grpc_channel_args * a,const grpc_channel_args * b)528 int grpc_channel_args_compare(const grpc_channel_args* a,
529                               const grpc_channel_args* b) {
530   if (a == nullptr && b == nullptr) return 0;
531   if (a == nullptr || b == nullptr) return a == nullptr ? -1 : 1;
532   int c = grpc_core::QsortCompare(a->num_args, b->num_args);
533   if (c != 0) return c;
534   for (size_t i = 0; i < a->num_args; i++) {
535     c = cmp_arg(&a->args[i], &b->args[i]);
536     if (c != 0) return c;
537   }
538   return 0;
539 }
540 
grpc_channel_args_find(const grpc_channel_args * args,const char * name)541 const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args,
542                                        const char* name) {
543   if (args != nullptr) {
544     for (size_t i = 0; i < args->num_args; ++i) {
545       if (strcmp(args->args[i].key, name) == 0) {
546         return &args->args[i];
547       }
548     }
549   }
550   return nullptr;
551 }
552 
grpc_channel_arg_get_integer(const grpc_arg * arg,const grpc_integer_options options)553 int grpc_channel_arg_get_integer(const grpc_arg* arg,
554                                  const grpc_integer_options options) {
555   if (arg == nullptr) return options.default_value;
556   if (arg->type != GRPC_ARG_INTEGER) {
557     LOG(ERROR) << arg->key << " ignored: it must be an integer";
558     return options.default_value;
559   }
560   if (arg->value.integer < options.min_value) {
561     LOG(ERROR) << arg->key << " ignored: it must be >= " << options.min_value;
562     return options.default_value;
563   }
564   if (arg->value.integer > options.max_value) {
565     LOG(ERROR) << arg->key << " ignored: it must be <= " << options.max_value;
566     return options.default_value;
567   }
568   return arg->value.integer;
569 }
570 
grpc_channel_args_find_integer(const grpc_channel_args * args,const char * name,const grpc_integer_options options)571 int grpc_channel_args_find_integer(const grpc_channel_args* args,
572                                    const char* name,
573                                    const grpc_integer_options options) {
574   const grpc_arg* arg = grpc_channel_args_find(args, name);
575   return grpc_channel_arg_get_integer(arg, options);
576 }
577 
grpc_channel_arg_get_string(const grpc_arg * arg)578 char* grpc_channel_arg_get_string(const grpc_arg* arg) {
579   if (arg == nullptr) return nullptr;
580   if (arg->type != GRPC_ARG_STRING) {
581     LOG(ERROR) << arg->key << " ignored: it must be an string";
582     return nullptr;
583   }
584   return arg->value.string;
585 }
586 
grpc_channel_args_find_string(const grpc_channel_args * args,const char * name)587 char* grpc_channel_args_find_string(const grpc_channel_args* args,
588                                     const char* name) {
589   const grpc_arg* arg = grpc_channel_args_find(args, name);
590   return grpc_channel_arg_get_string(arg);
591 }
592 
grpc_channel_arg_get_bool(const grpc_arg * arg,bool default_value)593 bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value) {
594   if (arg == nullptr) return default_value;
595   if (arg->type != GRPC_ARG_INTEGER) {
596     LOG(ERROR) << arg->key << " ignored: it must be an integer";
597     return default_value;
598   }
599   switch (arg->value.integer) {
600     case 0:
601       return false;
602     case 1:
603       return true;
604     default:
605       LOG(ERROR) << arg->key << " treated as bool but set to "
606                  << arg->value.integer << " (assuming true)";
607       return true;
608   }
609 }
610 
grpc_channel_args_find_bool(const grpc_channel_args * args,const char * name,bool default_value)611 bool grpc_channel_args_find_bool(const grpc_channel_args* args,
612                                  const char* name, bool default_value) {
613   const grpc_arg* arg = grpc_channel_args_find(args, name);
614   return grpc_channel_arg_get_bool(arg, default_value);
615 }
616 
grpc_channel_args_want_minimal_stack(const grpc_channel_args * args)617 bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args) {
618   return grpc_channel_arg_get_bool(
619       grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false);
620 }
621 
grpc_channel_arg_string_create(char * name,char * value)622 grpc_arg grpc_channel_arg_string_create(char* name, char* value) {
623   grpc_arg arg;
624   arg.type = GRPC_ARG_STRING;
625   arg.key = name;
626   arg.value.string = value;
627   return arg;
628 }
629 
grpc_channel_arg_integer_create(char * name,int value)630 grpc_arg grpc_channel_arg_integer_create(char* name, int value) {
631   grpc_arg arg;
632   arg.type = GRPC_ARG_INTEGER;
633   arg.key = name;
634   arg.value.integer = value;
635   return arg;
636 }
637 
grpc_channel_arg_pointer_create(char * name,void * value,const grpc_arg_pointer_vtable * vtable)638 grpc_arg grpc_channel_arg_pointer_create(
639     char* name, void* value, const grpc_arg_pointer_vtable* vtable) {
640   grpc_arg arg;
641   arg.type = GRPC_ARG_POINTER;
642   arg.key = name;
643   arg.value.pointer.p = value;
644   arg.value.pointer.vtable = vtable;
645   return arg;
646 }
647 
grpc_channel_args_string(const grpc_channel_args * args)648 std::string grpc_channel_args_string(const grpc_channel_args* args) {
649   return grpc_core::ChannelArgs::FromC(args).ToString();
650 }
651 
652 namespace grpc_core {
ChannelArgsBuiltinPrecondition(const grpc_channel_args * src)653 ChannelArgs ChannelArgsBuiltinPrecondition(const grpc_channel_args* src) {
654   if (src == nullptr) return ChannelArgs();
655   ChannelArgs output;
656   std::map<absl::string_view, std::vector<absl::string_view>>
657       concatenated_values;
658   for (size_t i = 0; i < src->num_args; i++) {
659     absl::string_view key = src->args[i].key;
660     // User-agent strings were traditionally multi-valued and concatenated.
661     // We preserve this behavior for backwards compatibility.
662     if (key == GRPC_ARG_PRIMARY_USER_AGENT_STRING ||
663         key == GRPC_ARG_SECONDARY_USER_AGENT_STRING) {
664       if (src->args[i].type != GRPC_ARG_STRING) {
665         LOG(ERROR) << "Channel argument '" << key << "' should be a string";
666       } else {
667         concatenated_values[key].push_back(src->args[i].value.string);
668       }
669       continue;
670     } else if (absl::StartsWith(key, "grpc.internal.")) {
671       continue;
672     }
673     if (!output.Contains(key)) {
674       output = output.Set(src->args[i]);
675     } else {
676       // Traditional grpc_channel_args_find behavior was to pick the first
677       // value.
678       // For compatibility with existing users, we will do the same here.
679     }
680   }
681   // Concatenate the concatenated values.
682   for (const auto& concatenated_value : concatenated_values) {
683     output = output.Set(concatenated_value.first,
684                         absl::StrJoin(concatenated_value.second, " "));
685   }
686   return output;
687 }
688 
689 }  // namespace grpc_core
690 
691 namespace {
692 grpc_channel_args_client_channel_creation_mutator g_mutator = nullptr;
693 }  // namespace
694 
grpc_channel_args_set_client_channel_creation_mutator(grpc_channel_args_client_channel_creation_mutator cb)695 void grpc_channel_args_set_client_channel_creation_mutator(
696     grpc_channel_args_client_channel_creation_mutator cb) {
697   DCHECK_EQ(g_mutator, nullptr);
698   g_mutator = cb;
699 }
700 grpc_channel_args_client_channel_creation_mutator
grpc_channel_args_get_client_channel_creation_mutator()701 grpc_channel_args_get_client_channel_creation_mutator() {
702   return g_mutator;
703 }
704