diff options
Diffstat (limited to 'src/vlib')
-rw-r--r-- | src/vlib/buffer.h | 2 | ||||
-rw-r--r-- | src/vlib/buffer_funcs.h | 64 |
2 files changed, 65 insertions, 1 deletions
diff --git a/src/vlib/buffer.h b/src/vlib/buffer.h index 493d111041e..02b170907ba 100644 --- a/src/vlib/buffer.h +++ b/src/vlib/buffer.h @@ -302,7 +302,7 @@ vlib_buffer_get_tail (vlib_buffer_t * b) * @return pointer to beginning of uninitialized data */ always_inline void * -vlib_buffer_put_uninit (vlib_buffer_t * b, u8 size) +vlib_buffer_put_uninit (vlib_buffer_t * b, u16 size) { void *p = vlib_buffer_get_tail (b); /* XXX make sure there's enough space */ diff --git a/src/vlib/buffer_funcs.h b/src/vlib/buffer_funcs.h index d15ef5702f8..e8ccc86f1a9 100644 --- a/src/vlib/buffer_funcs.h +++ b/src/vlib/buffer_funcs.h @@ -1267,6 +1267,70 @@ vlib_buffer_chain_compress (vlib_main_t * vm, (first->flags & VLIB_BUFFER_NEXT_PRESENT)); } +/** + * @brief linearize buffer chain - the first buffer is filled, if needed, + * buffers are allocated and filled, returns free space in last buffer or + * negative on failure + * + * @param[in] vm - vlib_main + * @param[in,out] first - first buffer in chain + */ +always_inline int +vlib_buffer_chain_linearize (vlib_main_t * vm, vlib_buffer_t * first) +{ + vlib_buffer_t *b = first; + vlib_buffer_free_list_t *fl = + vlib_buffer_get_free_list (vm, vlib_buffer_get_free_list_index (b)); + u32 buf_len = fl->n_data_bytes; + // free buffer chain starting from the second buffer + int free_count = (b->flags & VLIB_BUFFER_NEXT_PRESENT) != 0; + u32 chain_to_free = b->next_buffer; + + u32 len = vlib_buffer_length_in_chain (vm, b); + u32 free_len = buf_len - b->current_data - b->current_length; + int alloc_len = clib_max (len - free_len, 0); //use the free len in the first buffer + int n_buffers = (alloc_len + buf_len - 1) / buf_len; + u32 new_buffers[n_buffers]; + + u32 n_alloc = vlib_buffer_alloc (vm, new_buffers, n_buffers); + if (n_alloc != n_buffers) + { + vlib_buffer_free_no_next (vm, new_buffers, n_alloc); + return -1; + } + + vlib_buffer_t *s = b; + while (s->flags & VLIB_BUFFER_NEXT_PRESENT) + { + s = vlib_get_buffer (vm, s->next_buffer); + int d_free_len = buf_len - b->current_data - b->current_length; + ASSERT (d_free_len >= 0); + // chain buf and split write + u32 copy_len = clib_min (d_free_len, s->current_length); + u8 *d = vlib_buffer_put_uninit (b, copy_len); + clib_memcpy (d, vlib_buffer_get_current (s), copy_len); + int rest = s->current_length - copy_len; + if (rest > 0) + { + //prev buf is full + ASSERT (vlib_buffer_get_tail (b) == b->data + buf_len); + ASSERT (n_buffers > 0); + b = vlib_buffer_chain_buffer (vm, b, new_buffers[--n_buffers]); + //make full use of the new buffers + b->current_data = 0; + d = vlib_buffer_put_uninit (b, rest); + clib_memcpy (d, vlib_buffer_get_current (s) + copy_len, rest); + } + } + vlib_buffer_free (vm, &chain_to_free, free_count); + b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID; + if (b == first) /* no buffers addeed */ + b->flags &= ~VLIB_BUFFER_NEXT_PRESENT; + ASSERT (len == vlib_buffer_length_in_chain (vm, first)); + ASSERT (n_buffers == 0); + return buf_len - b->current_data - b->current_length; +} + #endif /* included_vlib_buffer_funcs_h */ /* |