| #ifndef __dispatch_buffer_field_api_h__ |
| #define __dispatch_buffer_field_api_h__ 1 |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /** @file |
| * Inline API for common operations on buffer_fields |
| * @internal |
| * @{ |
| */ |
| |
| #include "qpid/dispatch/buffer_field.h" |
| #include "qpid/dispatch/parse.h" |
| #include <stdbool.h> |
| |
| /* qd_buffer_field_normalize |
| * |
| * Invariant: a non-empty buffer fields cursor always points to a valid octet, |
| * never at the end of a non-terminal buffer. Normalizing ensures that |
| * invariant holds. |
| */ |
| static inline void qd_buffer_field_normalize(qd_buffer_field_t *bfield) |
| { |
| assert(bfield); |
| if (bfield->remaining) { |
| while (bfield->cursor == qd_buffer_cursor(bfield->buffer)) { |
| bfield->buffer = DEQ_NEXT(bfield->buffer); |
| assert(bfield->buffer); // error: remaining value incorrect |
| bfield->cursor = qd_buffer_base(bfield->buffer); |
| } |
| } |
| } |
| |
| |
| /* qd_buffer_field |
| * |
| * Constructor - ensures buffer field is well formed. |
| */ |
| static inline qd_buffer_field_t qd_buffer_field(qd_buffer_t *buffer, const uint8_t *cursor, size_t remaining) |
| { |
| assert(buffer); |
| qd_buffer_field_t bf = {.buffer = buffer, .cursor = cursor, .remaining = remaining}; |
| qd_buffer_field_normalize(&bf); |
| return bf; |
| } |
| |
| |
| /* qd_buffer_field_extend |
| * |
| * Increase the size of the field by amount octets |
| */ |
| static inline size_t qd_buffer_field_extend(qd_buffer_field_t *bfield, size_t amount) |
| { |
| assert(bfield); |
| size_t old = bfield->remaining; |
| bfield->remaining += amount; |
| if (old == 0) // move cursor to start of new data |
| qd_buffer_field_normalize(bfield); |
| return bfield->remaining; |
| } |
| |
| |
| /* qd_buffer_field_ncopy |
| * |
| * Copy up to n octets from bfield to dest, advance bfield by the number of |
| * octets copied |
| * |
| * @return total of octets copied - may be < n if len(bfield) < n |
| */ |
| static inline size_t qd_buffer_field_ncopy(qd_buffer_field_t *bfield, uint8_t *dest, size_t n) |
| { |
| assert(bfield); |
| |
| const uint8_t * const start = dest; |
| size_t count = MIN(n, bfield->remaining); |
| |
| while (count) { |
| size_t avail = qd_buffer_cursor(bfield->buffer) - bfield->cursor; |
| if (count < avail) { |
| // fastpath: no need to adjust buffer pointers |
| memcpy(dest, bfield->cursor, count); |
| dest += count; |
| bfield->cursor += count; |
| bfield->remaining -= count; |
| return dest - start; |
| } |
| |
| memcpy(dest, bfield->cursor, avail); |
| dest += avail; |
| count -= avail; |
| |
| // count is >= what is available in the current buffer, move to next |
| |
| bfield->remaining -= avail; |
| bfield->cursor += avail; |
| qd_buffer_field_normalize(bfield); |
| } |
| |
| return dest - start; |
| } |
| |
| |
| /* qd_buffer_field_advance |
| * |
| * Move the cursor of bfield forward by amount octets. |
| * |
| * @return total of octets skipped - may be < amount if len(bfield) < amount |
| */ |
| static inline size_t qd_buffer_field_advance(qd_buffer_field_t *bfield, size_t amount) |
| { |
| assert(bfield); |
| |
| const size_t blen = bfield->remaining; |
| size_t count = MIN(amount, blen); |
| |
| while (count > 0) { |
| size_t avail = qd_buffer_cursor(bfield->buffer) - bfield->cursor; |
| |
| if (count < avail) { |
| // fastpath: no need to adjust buffer pointers |
| bfield->cursor += count; |
| bfield->remaining -= count; |
| break; |
| } |
| |
| // count is >= what is available in the current buffer, move to next |
| count -= avail; |
| bfield->remaining -= avail; |
| bfield->cursor += avail; |
| qd_buffer_field_normalize(bfield); |
| } |
| |
| return blen - bfield->remaining; |
| } |
| |
| |
| /* qd_buffer_field_octet |
| * |
| * Get the first octet of the field and move the cursor to the next octet (if |
| * present). bfield length is decremented by 1 |
| * |
| * @return true of octet read, false if no octet available (end of field). |
| */ |
| static inline bool qd_buffer_field_octet(qd_buffer_field_t *bfield, uint8_t *octet) |
| { |
| assert(bfield); |
| |
| if (bfield->remaining) { |
| assert(bfield->cursor < qd_buffer_cursor(bfield->buffer)); |
| *octet = *bfield->cursor++; |
| bfield->remaining -= 1; |
| qd_buffer_field_normalize(bfield); |
| return true; |
| } |
| return false; |
| } |
| |
| |
| /* qd_buffer_field_uint32 |
| * |
| * Get the next 4 octets of the field and convert them to a uint32 value. Move |
| * the cursor past the 4 octets and decrement the length by 4. uint32 values |
| * are used extensively in the AMQP type encodings for meta data (size and |
| * count). |
| * |
| * @return true of uint32 read, false if not enough octets available (end of field). |
| */ |
| static inline bool qd_buffer_field_uint32(qd_buffer_field_t *bfield, uint32_t *value) |
| { |
| assert(bfield); |
| |
| if (bfield->remaining >= 4) { |
| uint8_t buf[4]; |
| qd_buffer_field_ncopy(bfield, buf, 4); |
| *value = qd_parse_uint32_decode(buf); |
| return true; |
| } |
| return false; |
| } |
| |
| |
| /* qd_buffer_field_strdup |
| * |
| * Return a null terminated string containing the bfield data. Caller assumes |
| * responsibility for calling free() on the returned value when finished with |
| * it. Caller also should ensure the data is actually a value that can be |
| * rendered as a C string (e.g. no internal zero values). |
| * |
| * @return null terminated C string, must be free()ed by caller. |
| */ |
| static inline char *qd_buffer_field_strdup(qd_buffer_field_t *bfield) |
| { |
| assert(bfield); |
| |
| const size_t len = bfield->remaining + 1; |
| char *str = qd_malloc(len); |
| qd_buffer_field_ncopy(bfield, (uint8_t*) str, bfield->remaining); |
| str[len - 1] = 0; |
| return str; |
| } |
| |
| |
| /* qd_buffer_field_equal |
| * |
| * Check if the field is exactly equal to count octets of data. If equal |
| * advance the bfield count octets. |
| * |
| * @return true if equal |
| */ |
| static inline bool qd_buffer_field_equal(qd_buffer_field_t *bfield, const uint8_t *data, size_t count) |
| { |
| assert(bfield); |
| |
| if (bfield->remaining < count) |
| return false; |
| |
| const qd_buffer_field_t save = *bfield; |
| |
| while (count) { |
| |
| size_t avail = qd_buffer_cursor(bfield->buffer) - bfield->cursor; |
| |
| if (count < avail) { |
| // fastpath: no need to adjust buffer pointers |
| if (memcmp(data, bfield->cursor, count) != 0) { |
| *bfield = save; |
| return false; |
| } |
| bfield->cursor += count; |
| bfield->remaining -= count; |
| return true; |
| } |
| |
| if (memcmp(data, bfield->cursor, avail) != 0) { |
| *bfield = save; |
| return false; |
| } |
| |
| data += avail; |
| count -= avail; |
| bfield->remaining -= avail; |
| bfield->cursor += avail; |
| qd_buffer_field_normalize(bfield); |
| } |
| |
| return true; |
| } |
| |
| |
| /* qd_buffer_list_append_field |
| * |
| * Copy the contents of bfield to the end of the buflist buffer chain. This |
| * copies all data - no bfield buffers are moved to buflist. bfield is advanced |
| * to the end of data. |
| * |
| * @return void |
| */ |
| static inline void qd_buffer_list_append_field(qd_buffer_list_t *buflist, qd_buffer_field_t *bfield) |
| { |
| assert(buflist); |
| assert(bfield); |
| |
| while (bfield->remaining) { |
| size_t avail = qd_buffer_cursor(bfield->buffer) - bfield->cursor; |
| size_t len = MIN(bfield->remaining, avail); |
| |
| qd_buffer_list_append(buflist, bfield->cursor, len); |
| bfield->remaining -= len; |
| if (!bfield->remaining) { |
| bfield->cursor += len; |
| } else { |
| bfield->buffer = DEQ_NEXT(bfield->buffer); |
| assert(bfield->buffer); |
| bfield->cursor = qd_buffer_base(bfield->buffer); |
| } |
| } |
| } |
| |
| ///@} |
| |
| #endif |