diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc index 1e027cb68ea..a4dfb2b4401 100644 --- a/src/core/lib/transport/metadata_batch.cc +++ b/src/core/lib/transport/metadata_batch.cc @@ -24,7 +24,6 @@ #include #include "absl/container/inlined_vector.h" -#include "absl/strings/str_join.h" #include #include @@ -33,345 +32,6 @@ #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" -namespace grpc_core { - -static void assert_valid_list(grpc_mdelem_list* list) { -#ifndef NDEBUG - grpc_linked_mdelem* l; - - GPR_ASSERT((list->head == nullptr) == (list->tail == nullptr)); - if (!list->head) return; - GPR_ASSERT(list->head->prev == nullptr); - GPR_ASSERT(list->tail->next == nullptr); - GPR_ASSERT((list->head == list->tail) == (list->head->next == nullptr)); - - size_t verified_count = 0; - for (l = list->head; l; l = l->next) { - GPR_ASSERT(!GRPC_MDISNULL(l->md)); - GPR_ASSERT((l->prev == nullptr) == (l == list->head)); - GPR_ASSERT((l->next == nullptr) == (l == list->tail)); - if (l->next) GPR_ASSERT(l->next->prev == l); - if (l->prev) GPR_ASSERT(l->prev->next == l); - verified_count++; - } - GPR_ASSERT(list->count == verified_count); -#else - // Avoid unused-parameter warning for debug-only parameter - (void)list; -#endif /* NDEBUG */ -} - -void MetadataMap::AssertValidCallouts() { -#ifndef NDEBUG - for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) { - grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md)); - grpc_metadata_batch_callouts_index callout_idx = - GRPC_BATCH_INDEX_OF(key_interned); - if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) { - GPR_ASSERT(idx_.array[callout_idx] == l); - } - grpc_slice_unref_internal(key_interned); - } -#endif -} - -#ifndef NDEBUG -void MetadataMap::AssertOk() { assert_valid_list(&list_); } -#endif /* NDEBUG */ - -MetadataMap::MetadataMap() { - memset(&list_, 0, sizeof(list_)); - memset(&idx_, 0, sizeof(idx_)); - deadline_ = GRPC_MILLIS_INF_FUTURE; -} - -MetadataMap::MetadataMap(MetadataMap&& other) noexcept { - list_ = other.list_; - idx_ = other.idx_; - deadline_ = other.deadline_; - memset(&other.list_, 0, sizeof(list_)); - memset(&other.idx_, 0, sizeof(idx_)); - other.deadline_ = GRPC_MILLIS_INF_FUTURE; -} - -MetadataMap& MetadataMap::operator=(MetadataMap&& other) noexcept { - Clear(); - list_ = other.list_; - idx_ = other.idx_; - deadline_ = other.deadline_; - memset(&other.list_, 0, sizeof(list_)); - memset(&other.idx_, 0, sizeof(idx_)); - other.deadline_ = GRPC_MILLIS_INF_FUTURE; - return *this; -} - -MetadataMap::~MetadataMap() { - AssertValidCallouts(); - for (auto* l = list_.head; l; l = l->next) { - GRPC_MDELEM_UNREF(l->md); - } -} - -static grpc_error_handle GPR_ATTRIBUTE_NOINLINE error_with_md(grpc_mdelem md) { - return grpc_attach_md_to_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), md); -} - -absl::optional MetadataMap::Remove(grpc_slice key) { - for (auto* l = list_.head; l; l = l->next) { - if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) { - auto out = grpc_slice_ref_internal(GRPC_MDVALUE(l->md)); - Remove(l); - return out; - } - } - return {}; -} - -grpc_error_handle MetadataMap::LinkCallout( - grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { - AssertValidCallouts(); - GPR_DEBUG_ASSERT(idx >= 0 && idx < GRPC_BATCH_CALLOUTS_COUNT); - if (GPR_LIKELY(idx_.array[idx] == nullptr)) { - ++list_.default_count; - idx_.array[idx] = storage; - AssertValidCallouts(); - return GRPC_ERROR_NONE; - } - AssertValidCallouts(); - return error_with_md(storage->md); -} - -grpc_error_handle MetadataMap::MaybeLinkCallout(grpc_linked_mdelem* storage) { - grpc_metadata_batch_callouts_index idx = - GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); - if (idx == GRPC_BATCH_CALLOUTS_COUNT) { - return GRPC_ERROR_NONE; - } - return LinkCallout(storage, idx); -} - -void MetadataMap::MaybeUnlinkCallout(grpc_linked_mdelem* storage) { - grpc_metadata_batch_callouts_index idx = - GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); - if (idx == GRPC_BATCH_CALLOUTS_COUNT) { - return; - } - --list_.default_count; - GPR_DEBUG_ASSERT(idx_.array[idx] != nullptr); - idx_.array[idx] = nullptr; -} - -grpc_error_handle MetadataMap::AddHead(grpc_linked_mdelem* storage, - grpc_mdelem elem_to_add) { - GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add)); - storage->md = elem_to_add; - return LinkHead(storage); -} - -static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) { - assert_valid_list(list); - GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md)); - storage->prev = nullptr; - storage->next = list->head; - storage->reserved = nullptr; - if (list->head != nullptr) { - list->head->prev = storage; - } else { - list->tail = storage; - } - list->head = storage; - list->count++; - assert_valid_list(list); -} - -grpc_error_handle MetadataMap::LinkHead(grpc_linked_mdelem* storage) { - AssertValidCallouts(); - grpc_error_handle err = MaybeLinkCallout(storage); - if (err != GRPC_ERROR_NONE) { - AssertValidCallouts(); - return err; - } - link_head(&list_, storage); - AssertValidCallouts(); - return GRPC_ERROR_NONE; -} - -// TODO(arjunroy): Need to revisit this and see what guarantees exist between -// C-core and the internal-metadata subsystem. E.g. can we ensure a particular -// metadata is never added twice, even in the presence of user supplied data? -grpc_error_handle MetadataMap::LinkHead( - grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { - GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx); - AssertValidCallouts(); - grpc_error_handle err = LinkCallout(storage, idx); - if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) { - AssertValidCallouts(); - return err; - } - link_head(&list_, storage); - AssertValidCallouts(); - return GRPC_ERROR_NONE; -} - -grpc_error_handle MetadataMap::AddTail(grpc_linked_mdelem* storage, - grpc_mdelem elem_to_add) { - GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add)); - storage->md = elem_to_add; - return LinkTail(storage); -} - -static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) { - assert_valid_list(list); - GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md)); - storage->prev = list->tail; - storage->next = nullptr; - storage->reserved = nullptr; - if (list->tail != nullptr) { - list->tail->next = storage; - } else { - list->head = storage; - } - list->tail = storage; - list->count++; - assert_valid_list(list); -} - -grpc_error_handle MetadataMap::LinkTail(grpc_linked_mdelem* storage) { - AssertValidCallouts(); - grpc_error_handle err = MaybeLinkCallout(storage); - if (err != GRPC_ERROR_NONE) { - AssertValidCallouts(); - return err; - } - link_tail(&list_, storage); - AssertValidCallouts(); - return GRPC_ERROR_NONE; -} - -grpc_error_handle MetadataMap::LinkTail( - grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { - GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx); - AssertValidCallouts(); - grpc_error_handle err = LinkCallout(storage, idx); - if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) { - AssertValidCallouts(); - return err; - } - link_tail(&list_, storage); - AssertValidCallouts(); - return GRPC_ERROR_NONE; -} - -static void unlink_storage(grpc_mdelem_list* list, - grpc_linked_mdelem* storage) { - assert_valid_list(list); - if (storage->prev != nullptr) { - storage->prev->next = storage->next; - } else { - list->head = storage->next; - } - if (storage->next != nullptr) { - storage->next->prev = storage->prev; - } else { - list->tail = storage->prev; - } - list->count--; - assert_valid_list(list); -} - -void MetadataMap::Remove(grpc_linked_mdelem* storage) { - AssertValidCallouts(); - MaybeUnlinkCallout(storage); - unlink_storage(&list_, storage); - GRPC_MDELEM_UNREF(storage->md); - AssertValidCallouts(); -} - -void MetadataMap::Remove(grpc_metadata_batch_callouts_index idx) { - AssertValidCallouts(); - if (idx_.array[idx] == nullptr) return; - --list_.default_count; - unlink_storage(&list_, idx_.array[idx]); - GRPC_MDELEM_UNREF(idx_.array[idx]->md); - idx_.array[idx] = nullptr; - AssertValidCallouts(); -} - -absl::optional MetadataMap::GetValue( - absl::string_view target_key, std::string* concatenated_value) const { - // Find all values for the specified key. - absl::InlinedVector values; - for (grpc_linked_mdelem* md = list_.head; md != nullptr; md = md->next) { - absl::string_view key = grpc_core::StringViewFromSlice(GRPC_MDKEY(md->md)); - absl::string_view value = - grpc_core::StringViewFromSlice(GRPC_MDVALUE(md->md)); - if (target_key == key) values.push_back(value); - } - // If none found, no match. - if (values.empty()) return absl::nullopt; - // If exactly one found, return it as-is. - if (values.size() == 1) return values.front(); - // If more than one found, concatenate the values, using - // *concatenated_values as a temporary holding place for the - // concatenated string. - *concatenated_value = absl::StrJoin(values, ","); - return *concatenated_value; -} - -grpc_error_handle MetadataMap::Substitute(grpc_linked_mdelem* storage, - grpc_mdelem new_mdelem) { - AssertValidCallouts(); - grpc_error_handle error = GRPC_ERROR_NONE; - grpc_mdelem old_mdelem = storage->md; - if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) { - MaybeUnlinkCallout(storage); - storage->md = new_mdelem; - error = MaybeLinkCallout(storage); - if (error != GRPC_ERROR_NONE) { - unlink_storage(&list_, storage); - GRPC_MDELEM_UNREF(storage->md); - } - } else { - storage->md = new_mdelem; - } - GRPC_MDELEM_UNREF(old_mdelem); - AssertValidCallouts(); - return error; -} - -void MetadataMap::Clear() { - this->~MetadataMap(); - new (this) MetadataMap(); -} - -size_t MetadataMap::TransportSize() const { - size_t size = 0; - for (grpc_linked_mdelem* elem = list_.head; elem != nullptr; - elem = elem->next) { - size += GRPC_MDELEM_LENGTH(elem->md); - } - return size; -} - -bool MetadataMap::ReplaceIfExists(grpc_slice key, grpc_slice value) { - AssertValidCallouts(); - for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) { - if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) { - auto new_mdelem = grpc_mdelem_from_slices(grpc_slice_ref_internal(key), - grpc_slice_ref_internal(value)); - GRPC_MDELEM_UNREF(l->md); - l->md = new_mdelem; - AssertValidCallouts(); - return true; - } - } - AssertValidCallouts(); - return false; -} - -} // namespace grpc_core - void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage, const grpc_slice& value) { grpc_mdelem old_mdelem = storage->md; diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index c8908051251..525e6f67960 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -23,6 +23,7 @@ #include +#include "absl/strings/str_join.h" #include "absl/types/optional.h" #include @@ -60,6 +61,9 @@ struct grpc_filtered_mdelem { #define GRPC_FILTERED_REMOVE() \ { GRPC_ERROR_NONE, GRPC_MDNULL } +grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src, + grpc_mdelem md); + namespace grpc_core { // MetadataMap encodes the mapping of metadata keys to metadata values. @@ -71,6 +75,7 @@ namespace grpc_core { // In the meantime, we're not going to invest much time in ephemeral API // documentation, so if you must use one of these API's and it's not obvious // how, reach out to ctiller. +template class MetadataMap { public: MetadataMap(); @@ -185,6 +190,88 @@ class MetadataMap { GRPC_MUST_USE_RESULT; void MaybeUnlinkCallout(grpc_linked_mdelem* storage); + static void assert_valid_list(grpc_mdelem_list* list) { +#ifndef NDEBUG + grpc_linked_mdelem* l; + + GPR_ASSERT((list->head == nullptr) == (list->tail == nullptr)); + if (!list->head) return; + GPR_ASSERT(list->head->prev == nullptr); + GPR_ASSERT(list->tail->next == nullptr); + GPR_ASSERT((list->head == list->tail) == (list->head->next == nullptr)); + + size_t verified_count = 0; + for (l = list->head; l; l = l->next) { + GPR_ASSERT(!GRPC_MDISNULL(l->md)); + GPR_ASSERT((l->prev == nullptr) == (l == list->head)); + GPR_ASSERT((l->next == nullptr) == (l == list->tail)); + if (l->next) GPR_ASSERT(l->next->prev == l); + if (l->prev) GPR_ASSERT(l->prev->next == l); + verified_count++; + } + GPR_ASSERT(list->count == verified_count); +#else + // Avoid unused-parameter warning for debug-only parameter + (void)list; +#endif /* NDEBUG */ + } + + static grpc_error_handle GPR_ATTRIBUTE_NOINLINE + error_with_md(grpc_mdelem md) { + return grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), + md); + } + + static void link_head(grpc_mdelem_list* list, grpc_linked_mdelem* storage) { + assert_valid_list(list); + GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md)); + storage->prev = nullptr; + storage->next = list->head; + storage->reserved = nullptr; + if (list->head != nullptr) { + list->head->prev = storage; + } else { + list->tail = storage; + } + list->head = storage; + list->count++; + assert_valid_list(list); + } + + static void link_tail(grpc_mdelem_list* list, grpc_linked_mdelem* storage) { + assert_valid_list(list); + GPR_DEBUG_ASSERT(!GRPC_MDISNULL(storage->md)); + storage->prev = list->tail; + storage->next = nullptr; + storage->reserved = nullptr; + if (list->tail != nullptr) { + list->tail->next = storage; + } else { + list->head = storage; + } + list->tail = storage; + list->count++; + assert_valid_list(list); + } + + static void unlink_storage(grpc_mdelem_list* list, + grpc_linked_mdelem* storage) { + assert_valid_list(list); + if (storage->prev != nullptr) { + storage->prev->next = storage->next; + } else { + list->head = storage->next; + } + if (storage->next != nullptr) { + storage->next->prev = storage->prev; + } else { + list->tail = storage->prev; + } + list->count--; + assert_valid_list(list); + } + /** Metadata elements in this batch */ grpc_mdelem_list list_; grpc_metadata_batch_callouts idx_; @@ -194,9 +281,293 @@ class MetadataMap { grpc_millis deadline_; }; +template +void MetadataMap::AssertValidCallouts() { +#ifndef NDEBUG + for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) { + grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md)); + grpc_metadata_batch_callouts_index callout_idx = + GRPC_BATCH_INDEX_OF(key_interned); + if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) { + GPR_ASSERT(idx_.array[callout_idx] == l); + } + grpc_slice_unref_internal(key_interned); + } +#endif +} + +#ifndef NDEBUG +template +void MetadataMap::AssertOk() { + assert_valid_list(&list_); +} +#endif /* NDEBUG */ + +template +MetadataMap::MetadataMap() { + memset(&list_, 0, sizeof(list_)); + memset(&idx_, 0, sizeof(idx_)); + deadline_ = GRPC_MILLIS_INF_FUTURE; +} + +template +MetadataMap::MetadataMap(MetadataMap&& other) noexcept { + list_ = other.list_; + idx_ = other.idx_; + deadline_ = other.deadline_; + memset(&other.list_, 0, sizeof(list_)); + memset(&other.idx_, 0, sizeof(idx_)); + other.deadline_ = GRPC_MILLIS_INF_FUTURE; +} + +template +MetadataMap& MetadataMap::operator=( + MetadataMap&& other) noexcept { + Clear(); + list_ = other.list_; + idx_ = other.idx_; + deadline_ = other.deadline_; + memset(&other.list_, 0, sizeof(list_)); + memset(&other.idx_, 0, sizeof(idx_)); + other.deadline_ = GRPC_MILLIS_INF_FUTURE; + return *this; +} + +template +MetadataMap::~MetadataMap() { + AssertValidCallouts(); + for (auto* l = list_.head; l; l = l->next) { + GRPC_MDELEM_UNREF(l->md); + } +} + +template +absl::optional MetadataMap::Remove(grpc_slice key) { + for (auto* l = list_.head; l; l = l->next) { + if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) { + auto out = grpc_slice_ref_internal(GRPC_MDVALUE(l->md)); + Remove(l); + return out; + } + } + return {}; +} + +template +grpc_error_handle MetadataMap::LinkCallout( + grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { + AssertValidCallouts(); + GPR_DEBUG_ASSERT(idx >= 0 && idx < GRPC_BATCH_CALLOUTS_COUNT); + if (GPR_LIKELY(idx_.array[idx] == nullptr)) { + ++list_.default_count; + idx_.array[idx] = storage; + AssertValidCallouts(); + return GRPC_ERROR_NONE; + } + AssertValidCallouts(); + return error_with_md(storage->md); +} + +template +grpc_error_handle MetadataMap::MaybeLinkCallout( + grpc_linked_mdelem* storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return GRPC_ERROR_NONE; + } + return LinkCallout(storage, idx); +} + +template +void MetadataMap::MaybeUnlinkCallout(grpc_linked_mdelem* storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return; + } + --list_.default_count; + GPR_DEBUG_ASSERT(idx_.array[idx] != nullptr); + idx_.array[idx] = nullptr; +} + +template +grpc_error_handle MetadataMap::AddHead(grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) { + GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return LinkHead(storage); +} + +template +grpc_error_handle MetadataMap::LinkHead( + grpc_linked_mdelem* storage) { + AssertValidCallouts(); + grpc_error_handle err = MaybeLinkCallout(storage); + if (err != GRPC_ERROR_NONE) { + AssertValidCallouts(); + return err; + } + link_head(&list_, storage); + AssertValidCallouts(); + return GRPC_ERROR_NONE; +} + +// TODO(arjunroy): Need to revisit this and see what guarantees exist between +// C-core and the internal-metadata subsystem. E.g. can we ensure a particular +// metadata is never added twice, even in the presence of user supplied data? +template +grpc_error_handle MetadataMap::LinkHead( + grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { + GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx); + AssertValidCallouts(); + grpc_error_handle err = LinkCallout(storage, idx); + if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) { + AssertValidCallouts(); + return err; + } + link_head(&list_, storage); + AssertValidCallouts(); + return GRPC_ERROR_NONE; +} + +template +grpc_error_handle MetadataMap::AddTail(grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) { + GPR_DEBUG_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return LinkTail(storage); +} + +template +grpc_error_handle MetadataMap::LinkTail( + grpc_linked_mdelem* storage) { + AssertValidCallouts(); + grpc_error_handle err = MaybeLinkCallout(storage); + if (err != GRPC_ERROR_NONE) { + AssertValidCallouts(); + return err; + } + link_tail(&list_, storage); + AssertValidCallouts(); + return GRPC_ERROR_NONE; +} + +template +grpc_error_handle MetadataMap::LinkTail( + grpc_linked_mdelem* storage, grpc_metadata_batch_callouts_index idx) { + GPR_DEBUG_ASSERT(GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)) == idx); + AssertValidCallouts(); + grpc_error_handle err = LinkCallout(storage, idx); + if (GPR_UNLIKELY(err != GRPC_ERROR_NONE)) { + AssertValidCallouts(); + return err; + } + link_tail(&list_, storage); + AssertValidCallouts(); + return GRPC_ERROR_NONE; +} + +template +void MetadataMap::Remove(grpc_linked_mdelem* storage) { + AssertValidCallouts(); + MaybeUnlinkCallout(storage); + unlink_storage(&list_, storage); + GRPC_MDELEM_UNREF(storage->md); + AssertValidCallouts(); +} + +template +void MetadataMap::Remove(grpc_metadata_batch_callouts_index idx) { + AssertValidCallouts(); + if (idx_.array[idx] == nullptr) return; + --list_.default_count; + unlink_storage(&list_, idx_.array[idx]); + GRPC_MDELEM_UNREF(idx_.array[idx]->md); + idx_.array[idx] = nullptr; + AssertValidCallouts(); +} + +template +absl::optional MetadataMap::GetValue( + absl::string_view target_key, std::string* concatenated_value) const { + // Find all values for the specified key. + absl::InlinedVector values; + for (grpc_linked_mdelem* md = list_.head; md != nullptr; md = md->next) { + absl::string_view key = grpc_core::StringViewFromSlice(GRPC_MDKEY(md->md)); + absl::string_view value = + grpc_core::StringViewFromSlice(GRPC_MDVALUE(md->md)); + if (target_key == key) values.push_back(value); + } + // If none found, no match. + if (values.empty()) return absl::nullopt; + // If exactly one found, return it as-is. + if (values.size() == 1) return values.front(); + // If more than one found, concatenate the values, using + // *concatenated_values as a temporary holding place for the + // concatenated string. + *concatenated_value = absl::StrJoin(values, ","); + return *concatenated_value; +} + +template +grpc_error_handle MetadataMap::Substitute( + grpc_linked_mdelem* storage, grpc_mdelem new_mdelem) { + AssertValidCallouts(); + grpc_error_handle error = GRPC_ERROR_NONE; + grpc_mdelem old_mdelem = storage->md; + if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) { + MaybeUnlinkCallout(storage); + storage->md = new_mdelem; + error = MaybeLinkCallout(storage); + if (error != GRPC_ERROR_NONE) { + unlink_storage(&list_, storage); + GRPC_MDELEM_UNREF(storage->md); + } + } else { + storage->md = new_mdelem; + } + GRPC_MDELEM_UNREF(old_mdelem); + AssertValidCallouts(); + return error; +} + +template +void MetadataMap::Clear() { + this->~MetadataMap(); + new (this) MetadataMap(); +} + +template +size_t MetadataMap::TransportSize() const { + size_t size = 0; + for (grpc_linked_mdelem* elem = list_.head; elem != nullptr; + elem = elem->next) { + size += GRPC_MDELEM_LENGTH(elem->md); + } + return size; +} + +template +bool MetadataMap::ReplaceIfExists(grpc_slice key, grpc_slice value) { + AssertValidCallouts(); + for (grpc_linked_mdelem* l = list_.head; l != nullptr; l = l->next) { + if (grpc_slice_eq(GRPC_MDKEY(l->md), key)) { + auto new_mdelem = grpc_mdelem_from_slices(grpc_slice_ref_internal(key), + grpc_slice_ref_internal(value)); + GRPC_MDELEM_UNREF(l->md); + l->md = new_mdelem; + AssertValidCallouts(); + return true; + } + } + AssertValidCallouts(); + return false; +} + } // namespace grpc_core -using grpc_metadata_batch = grpc_core::MetadataMap; +using grpc_metadata_batch = grpc_core::MetadataMap<>; inline void grpc_metadata_batch_clear(grpc_metadata_batch* batch) { batch->Clear(); @@ -329,9 +700,6 @@ inline grpc_error_handle GRPC_MUST_USE_RESULT grpc_metadata_batch_add_tail( return grpc_metadata_batch_add_tail(batch, storage, idx); } -grpc_error_handle grpc_attach_md_to_error(grpc_error_handle src, - grpc_mdelem md); - typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)( void* user_data, grpc_mdelem elem); inline GRPC_MUST_USE_RESULT grpc_error_handle grpc_metadata_batch_filter(