/* * * Copyright 2014, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "src/core/channel/metadata_buffer.h" #include #include #include #include #define INITIAL_ELEM_CAP 8 /* One queued call; we track offsets to string data in a shared buffer to reduce allocations. See grpc_metadata_buffer_impl for the memory use strategy */ typedef struct { grpc_mdelem *md; void (*cb)(void *user_data, grpc_op_error error); void *user_data; gpr_uint32 flags; } qelem; /* Memory layout: grpc_metadata_buffer_impl followed by an array of qelem */ struct grpc_metadata_buffer_impl { /* number of elements in q */ size_t elems; /* capacity of q */ size_t elem_cap; }; #define ELEMS(buffer) ((qelem *)((buffer)+1)) void grpc_metadata_buffer_init(grpc_metadata_buffer *buffer) { /* start buffer as NULL, indicating no elements */ *buffer = NULL; } void grpc_metadata_buffer_destroy(grpc_metadata_buffer *buffer, grpc_op_error error) { size_t i; qelem *qe; if (*buffer) { for (i = 0; i < (*buffer)->elems; i++) { qe = &ELEMS(*buffer)[i]; grpc_mdelem_unref(qe->md); qe->cb(qe->user_data, error); } gpr_free(*buffer); } } void grpc_metadata_buffer_queue(grpc_metadata_buffer *buffer, grpc_call_op *op) { grpc_metadata_buffer_impl *impl = *buffer; qelem *qe; size_t bytes; GPR_ASSERT(op->type == GRPC_SEND_METADATA || op->type == GRPC_RECV_METADATA); if (!impl) { /* this is the first element: allocate enough space to hold the header object and the initial element capacity of qelems */ bytes = sizeof(grpc_metadata_buffer_impl) + INITIAL_ELEM_CAP * sizeof(qelem); impl = gpr_malloc(bytes); /* initialize the header object */ impl->elems = 0; impl->elem_cap = INITIAL_ELEM_CAP; } else if (impl->elems == impl->elem_cap) { /* more qelems than what we can deal with: grow by doubling size */ impl->elem_cap *= 2; bytes = sizeof(grpc_metadata_buffer_impl) + impl->elem_cap * sizeof(qelem); impl = gpr_realloc(impl, bytes); } /* append an element to the queue */ qe = &ELEMS(impl)[impl->elems]; impl->elems++; qe->md = op->data.metadata; qe->cb = op->done_cb; qe->user_data = op->user_data; qe->flags = op->flags; /* header object may have changed location: store it back */ *buffer = impl; } void grpc_metadata_buffer_flush(grpc_metadata_buffer *buffer, grpc_call_element *elem) { grpc_metadata_buffer_impl *impl = *buffer; grpc_call_op op; qelem *qe; size_t i; if (!impl) { /* nothing to send */ return; } /* construct call_op's, and push them down the stack */ op.type = GRPC_SEND_METADATA; op.dir = GRPC_CALL_DOWN; for (i = 0; i < impl->elems; i++) { qe = &ELEMS(impl)[i]; op.done_cb = qe->cb; op.user_data = qe->user_data; op.flags = qe->flags; op.data.metadata = qe->md; grpc_call_next_op(elem, &op); } /* free data structures and reset to NULL: we can only flush once */ gpr_free(impl); *buffer = NULL; } size_t grpc_metadata_buffer_count(const grpc_metadata_buffer *buffer) { return *buffer ? (*buffer)->elems : 0; } typedef struct { grpc_metadata_buffer_impl *impl; } elems_hdr; grpc_metadata *grpc_metadata_buffer_extract_elements( grpc_metadata_buffer *buffer) { grpc_metadata_buffer_impl *impl; elems_hdr *hdr; qelem *src; grpc_metadata *out; size_t i; impl = *buffer; if (!impl) { return NULL; } hdr = gpr_malloc(sizeof(elems_hdr) + impl->elems * sizeof(grpc_metadata)); src = ELEMS(impl); out = (grpc_metadata *)(hdr + 1); hdr->impl = impl; for (i = 0; i < impl->elems; i++) { out[i].key = (char *)grpc_mdstr_as_c_string(src[i].md->key); out[i].value = (char *)grpc_mdstr_as_c_string(src[i].md->value); out[i].value_length = GPR_SLICE_LENGTH(src[i].md->value->slice); } /* clear out buffer (it's not possible to extract elements twice */ *buffer = NULL; return out; } void grpc_metadata_buffer_cleanup_elements(void *elements, grpc_op_error error) { elems_hdr *hdr = ((elems_hdr *)elements) - 1; if (!elements) { return; } grpc_metadata_buffer_destroy(&hdr->impl, error); gpr_free(hdr); }