| /* |
| * |
| * 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 <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/useful.h> |
| |
| #include <string.h> |
| |
| #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); |
| } |