| /* |
| * Copyright (c) 2015 Cisco and/or its affiliates. |
| * Licensed 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. |
| */ |
| /* |
| Copyright (c) 2005 Eliot Dresselhaus |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /* Turn data structures into byte streams for saving or transport. */ |
| |
| #include <vppinfra/heap.h> |
| #include <vppinfra/pool.h> |
| #include <vppinfra/serialize.h> |
| |
| void |
| serialize_64 (serialize_main_t * m, va_list * va) |
| { |
| u64 x = va_arg (*va, u64); |
| u32 lo, hi; |
| lo = x; |
| hi = x >> 32; |
| serialize_integer (m, lo, sizeof (lo)); |
| serialize_integer (m, hi, sizeof (hi)); |
| } |
| |
| void |
| serialize_32 (serialize_main_t * m, va_list * va) |
| { |
| u32 x = va_arg (*va, u32); |
| serialize_integer (m, x, sizeof (x)); |
| } |
| |
| void |
| serialize_16 (serialize_main_t * m, va_list * va) |
| { |
| u32 x = va_arg (*va, u32); |
| serialize_integer (m, x, sizeof (u16)); |
| } |
| |
| void |
| serialize_8 (serialize_main_t * m, va_list * va) |
| { |
| u32 x = va_arg (*va, u32); |
| serialize_integer (m, x, sizeof (u8)); |
| } |
| |
| void |
| unserialize_64 (serialize_main_t * m, va_list * va) |
| { |
| u64 *x = va_arg (*va, u64 *); |
| u32 lo, hi; |
| unserialize_integer (m, &lo, sizeof (lo)); |
| unserialize_integer (m, &hi, sizeof (hi)); |
| *x = ((u64) hi << 32) | (u64) lo; |
| } |
| |
| void |
| unserialize_32 (serialize_main_t * m, va_list * va) |
| { |
| u32 *x = va_arg (*va, u32 *); |
| unserialize_integer (m, x, sizeof (x[0])); |
| } |
| |
| void |
| unserialize_16 (serialize_main_t * m, va_list * va) |
| { |
| u16 *x = va_arg (*va, u16 *); |
| u32 t; |
| unserialize_integer (m, &t, sizeof (x[0])); |
| x[0] = t; |
| } |
| |
| void |
| unserialize_8 (serialize_main_t * m, va_list * va) |
| { |
| u8 *x = va_arg (*va, u8 *); |
| u32 t; |
| unserialize_integer (m, &t, sizeof (x[0])); |
| x[0] = t; |
| } |
| |
| void |
| serialize_f64 (serialize_main_t * m, va_list * va) |
| { |
| f64 x = va_arg (*va, f64); |
| union |
| { |
| f64 f; |
| u64 i; |
| } y; |
| y.f = x; |
| serialize (m, serialize_64, y.i); |
| } |
| |
| void |
| serialize_f32 (serialize_main_t * m, va_list * va) |
| { |
| f32 x = va_arg (*va, f64); |
| union |
| { |
| f32 f; |
| u32 i; |
| } y; |
| y.f = x; |
| serialize_integer (m, y.i, sizeof (y.i)); |
| } |
| |
| void |
| unserialize_f64 (serialize_main_t * m, va_list * va) |
| { |
| f64 *x = va_arg (*va, f64 *); |
| union |
| { |
| f64 f; |
| u64 i; |
| } y; |
| unserialize (m, unserialize_64, &y.i); |
| *x = y.f; |
| } |
| |
| void |
| unserialize_f32 (serialize_main_t * m, va_list * va) |
| { |
| f32 *x = va_arg (*va, f32 *); |
| union |
| { |
| f32 f; |
| u32 i; |
| } y; |
| unserialize_integer (m, &y.i, sizeof (y.i)); |
| *x = y.f; |
| } |
| |
| __clib_export void |
| serialize_cstring (serialize_main_t * m, char *s) |
| { |
| u32 len = s ? strlen (s) : 0; |
| void *p; |
| |
| serialize_likely_small_unsigned_integer (m, len); |
| if (len > 0) |
| { |
| p = serialize_get (m, len); |
| clib_memcpy_fast (p, s, len); |
| } |
| } |
| |
| __clib_export void |
| unserialize_cstring (serialize_main_t * m, char **s) |
| { |
| char *p, *r = 0; |
| u32 len; |
| |
| len = unserialize_likely_small_unsigned_integer (m); |
| |
| /* |
| * Given broken enough data, we could get len = 0xFFFFFFFF. |
| * Add one, it overflows, we call vec_new (char, 0), then |
| * memcpy until we bus error. |
| */ |
| if (len > 0 && len != 0xFFFFFFFF) |
| { |
| r = vec_new (char, len + 1); |
| p = unserialize_get (m, len); |
| clib_memcpy_fast (r, p, len); |
| |
| /* Null terminate. */ |
| r[len] = 0; |
| } |
| *s = r; |
| } |
| |
| /* vec_serialize/vec_unserialize helper functions for basic vector types. */ |
| void |
| serialize_vec_8 (serialize_main_t * m, va_list * va) |
| { |
| u8 *s = va_arg (*va, u8 *); |
| u32 n = va_arg (*va, u32); |
| u8 *p = serialize_get (m, n * sizeof (u8)); |
| clib_memcpy_fast (p, s, n * sizeof (u8)); |
| } |
| |
| void |
| unserialize_vec_8 (serialize_main_t * m, va_list * va) |
| { |
| u8 *s = va_arg (*va, u8 *); |
| u32 n = va_arg (*va, u32); |
| u8 *p = unserialize_get (m, n); |
| clib_memcpy_fast (s, p, n); |
| } |
| |
| #define _(n_bits) \ |
| void serialize_vec_##n_bits (serialize_main_t * m, va_list * va) \ |
| { \ |
| u##n_bits * s = va_arg (*va, u##n_bits *); \ |
| u32 n = va_arg (*va, u32); \ |
| u##n_bits * p = serialize_get (m, n * sizeof (s[0])); \ |
| \ |
| while (n >= 4) \ |
| { \ |
| p[0] = clib_host_to_net_u##n_bits (s[0]); \ |
| p[1] = clib_host_to_net_u##n_bits (s[1]); \ |
| p[2] = clib_host_to_net_u##n_bits (s[2]); \ |
| p[3] = clib_host_to_net_u##n_bits (s[3]); \ |
| s += 4; \ |
| p += 4; \ |
| n -= 4; \ |
| } \ |
| \ |
| while (n >= 1) \ |
| { \ |
| p[0] = clib_host_to_net_u##n_bits (s[0]); \ |
| s += 1; \ |
| p += 1; \ |
| n -= 1; \ |
| } \ |
| } \ |
| \ |
| void unserialize_vec_##n_bits (serialize_main_t * m, va_list * va) \ |
| { \ |
| u##n_bits * s = va_arg (*va, u##n_bits *); \ |
| u32 n = va_arg (*va, u32); \ |
| u##n_bits * p = unserialize_get (m, n * sizeof (s[0])); \ |
| \ |
| while (n >= 4) \ |
| { \ |
| s[0] = clib_net_to_host_mem_u##n_bits (&p[0]); \ |
| s[1] = clib_net_to_host_mem_u##n_bits (&p[1]); \ |
| s[2] = clib_net_to_host_mem_u##n_bits (&p[2]); \ |
| s[3] = clib_net_to_host_mem_u##n_bits (&p[3]); \ |
| s += 4; \ |
| p += 4; \ |
| n -= 4; \ |
| } \ |
| \ |
| while (n >= 1) \ |
| { \ |
| s[0] = clib_net_to_host_mem_u##n_bits (&p[0]); \ |
| s += 1; \ |
| p += 1; \ |
| n -= 1; \ |
| } \ |
| } |
| |
| _(16); |
| _(32); |
| _(64); |
| |
| #undef _ |
| |
| #define SERIALIZE_VECTOR_CHUNK_SIZE 64 |
| |
| __clib_export void |
| serialize_vector (serialize_main_t * m, va_list * va) |
| { |
| void *vec = va_arg (*va, void *); |
| u32 elt_bytes = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| u32 l = vec_len (vec); |
| void *p = vec; |
| |
| serialize_integer (m, l, sizeof (l)); |
| |
| /* Serialize vector in chunks for cache locality. */ |
| while (l != 0) |
| { |
| u32 n = clib_min (SERIALIZE_VECTOR_CHUNK_SIZE, l); |
| serialize (m, f, p, n); |
| l -= n; |
| p += SERIALIZE_VECTOR_CHUNK_SIZE * elt_bytes; |
| } |
| } |
| |
| void * |
| unserialize_vector_ha (serialize_main_t * m, |
| u32 elt_bytes, |
| u32 header_bytes, |
| u32 align, u32 max_length, serialize_function_t * f) |
| { |
| void *v, *p; |
| u32 l; |
| |
| unserialize_integer (m, &l, sizeof (l)); |
| if (l > max_length) |
| serialize_error (&m->header, |
| clib_error_create ("bad vector length %d", l)); |
| p = v = _vec_realloc ((void *) 0, l, elt_bytes, header_bytes, |
| /* align */ align, 0); |
| |
| while (l != 0) |
| { |
| u32 n = clib_min (SERIALIZE_VECTOR_CHUNK_SIZE, l); |
| unserialize (m, f, p, n); |
| l -= n; |
| p += SERIALIZE_VECTOR_CHUNK_SIZE * elt_bytes; |
| } |
| return v; |
| } |
| |
| void |
| unserialize_aligned_vector (serialize_main_t * m, va_list * va) |
| { |
| void **vec = va_arg (*va, void **); |
| u32 elt_bytes = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| u32 align = va_arg (*va, u32); |
| |
| *vec = unserialize_vector_ha (m, elt_bytes, |
| /* header_bytes */ 0, |
| /* align */ align, |
| /* max_length */ ~0, |
| f); |
| } |
| |
| __clib_export void |
| unserialize_vector (serialize_main_t * m, va_list * va) |
| { |
| void **vec = va_arg (*va, void **); |
| u32 elt_bytes = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| |
| *vec = unserialize_vector_ha (m, elt_bytes, |
| /* header_bytes */ 0, |
| /* align */ 0, |
| /* max_length */ ~0, |
| f); |
| } |
| |
| void |
| serialize_bitmap (serialize_main_t * m, uword * b) |
| { |
| u32 l, i, n_u32s; |
| |
| l = vec_len (b); |
| n_u32s = l * sizeof (b[0]) / sizeof (u32); |
| serialize_integer (m, n_u32s, sizeof (n_u32s)); |
| |
| /* Send 32 bit words, low-order word first on 64 bit. */ |
| for (i = 0; i < l; i++) |
| { |
| serialize_integer (m, b[i], sizeof (u32)); |
| if (BITS (uword) == 64) |
| serialize_integer (m, (u64) b[i] >> (u64) 32, sizeof (u32)); |
| } |
| } |
| |
| uword * |
| unserialize_bitmap (serialize_main_t * m) |
| { |
| uword *b = 0; |
| u32 i, n_u32s; |
| |
| unserialize_integer (m, &n_u32s, sizeof (n_u32s)); |
| if (n_u32s == 0) |
| return b; |
| |
| i = (n_u32s * sizeof (u32) + sizeof (b[0]) - 1) / sizeof (b[0]); |
| vec_resize (b, i); |
| for (i = 0; i < n_u32s; i++) |
| { |
| u32 data; |
| unserialize_integer (m, &data, sizeof (u32)); |
| |
| /* Low-word is first on 64 bit. */ |
| if (BITS (uword) == 64) |
| { |
| if ((i % 2) == 0) |
| b[i / 2] |= (u64) data << (u64) 0; |
| else |
| b[i / 2] |= (u64) data << (u64) 32; |
| } |
| else |
| { |
| b[i] = data; |
| } |
| } |
| |
| return b; |
| } |
| |
| void |
| serialize_pool (serialize_main_t * m, va_list * va) |
| { |
| void *pool = va_arg (*va, void *); |
| u32 elt_bytes = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| u32 l, lo, hi; |
| pool_header_t *p; |
| |
| l = vec_len (pool); |
| serialize_integer (m, l, sizeof (u32)); |
| if (l == 0) |
| return; |
| p = pool_header (pool); |
| |
| /* No need to send free bitmap. Need to send index vector |
| to guarantee that unserialized pool will be identical. */ |
| vec_serialize (m, p->free_indices, serialize_vec_32); |
| |
| pool_foreach_region (lo, hi, pool, |
| serialize (m, f, pool + lo * elt_bytes, hi - lo)); |
| } |
| |
| static void * |
| unserialize_pool_helper (serialize_main_t * m, |
| u32 elt_bytes, u32 align, serialize_function_t * f) |
| { |
| void *v; |
| u32 i, l, lo, hi; |
| pool_header_t *p; |
| |
| unserialize_integer (m, &l, sizeof (l)); |
| if (l == 0) |
| { |
| return 0; |
| } |
| |
| v = _vec_realloc ((void *) 0, l, elt_bytes, sizeof (p[0]), align, 0); |
| p = pool_header (v); |
| |
| vec_unserialize (m, &p->free_indices, unserialize_vec_32); |
| |
| /* Construct free bitmap. */ |
| p->free_bitmap = 0; |
| for (i = 0; i < vec_len (p->free_indices); i++) |
| p->free_bitmap = clib_bitmap_ori (p->free_bitmap, p->free_indices[i]); |
| |
| pool_foreach_region (lo, hi, v, |
| unserialize (m, f, v + lo * elt_bytes, hi - lo)); |
| |
| return v; |
| } |
| |
| void |
| unserialize_pool (serialize_main_t * m, va_list * va) |
| { |
| void **result = va_arg (*va, void **); |
| u32 elt_bytes = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| *result = unserialize_pool_helper (m, elt_bytes, /* align */ 0, f); |
| } |
| |
| void |
| unserialize_aligned_pool (serialize_main_t * m, va_list * va) |
| { |
| void **result = va_arg (*va, void **); |
| u32 elt_bytes = va_arg (*va, u32); |
| u32 align = va_arg (*va, u32); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| *result = unserialize_pool_helper (m, elt_bytes, align, f); |
| } |
| |
| static void |
| serialize_vec_heap_elt (serialize_main_t * m, va_list * va) |
| { |
| heap_elt_t *e = va_arg (*va, heap_elt_t *); |
| u32 i, n = va_arg (*va, u32); |
| for (i = 0; i < n; i++) |
| { |
| serialize_integer (m, e[i].offset, sizeof (e[i].offset)); |
| serialize_integer (m, e[i].next, sizeof (e[i].next)); |
| serialize_integer (m, e[i].prev, sizeof (e[i].prev)); |
| } |
| } |
| |
| static void |
| unserialize_vec_heap_elt (serialize_main_t * m, va_list * va) |
| { |
| heap_elt_t *e = va_arg (*va, heap_elt_t *); |
| u32 i, n = va_arg (*va, u32); |
| for (i = 0; i < n; i++) |
| { |
| unserialize_integer (m, &e[i].offset, sizeof (e[i].offset)); |
| unserialize_integer (m, &e[i].next, sizeof (e[i].next)); |
| unserialize_integer (m, &e[i].prev, sizeof (e[i].prev)); |
| } |
| } |
| |
| void |
| serialize_heap (serialize_main_t * m, va_list * va) |
| { |
| void *heap = va_arg (*va, void *); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| u32 i, l; |
| heap_header_t *h; |
| |
| l = vec_len (heap); |
| serialize_integer (m, l, sizeof (u32)); |
| if (l == 0) |
| return; |
| |
| h = heap_header (heap); |
| |
| #define foreach_serialize_heap_header_integer \ |
| _ (head) _ (tail) _ (used_count) _ (max_len) _ (flags) _ (elt_bytes) |
| |
| #define _(f) serialize_integer (m, h->f, sizeof (h->f)); |
| foreach_serialize_heap_header_integer; |
| #undef _ |
| |
| serialize_integer (m, vec_len (h->free_lists), sizeof (u32)); |
| for (i = 0; i < vec_len (h->free_lists); i++) |
| vec_serialize (m, h->free_lists[i], serialize_vec_32); |
| |
| vec_serialize (m, h->elts, serialize_vec_heap_elt); |
| vec_serialize (m, h->small_free_elt_free_index, serialize_vec_32); |
| vec_serialize (m, h->free_elts, serialize_vec_32); |
| |
| /* Serialize data in heap. */ |
| { |
| heap_elt_t *e, *end; |
| e = h->elts + h->head; |
| end = h->elts + h->tail; |
| while (1) |
| { |
| if (!heap_is_free (e)) |
| { |
| void *v = heap + heap_offset (e) * h->elt_bytes; |
| u32 n = heap_elt_size (heap, e); |
| serialize (m, f, v, n); |
| } |
| if (e == end) |
| break; |
| e = heap_next (e); |
| } |
| } |
| } |
| |
| void |
| unserialize_heap (serialize_main_t * m, va_list * va) |
| { |
| void **result = va_arg (*va, void **); |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| u32 i, vl, fl; |
| heap_header_t h; |
| void *heap; |
| |
| unserialize_integer (m, &vl, sizeof (u32)); |
| if (vl == 0) |
| { |
| *result = 0; |
| return; |
| } |
| |
| clib_memset (&h, 0, sizeof (h)); |
| #define _(f) unserialize_integer (m, &h.f, sizeof (h.f)); |
| foreach_serialize_heap_header_integer; |
| #undef _ |
| |
| unserialize_integer (m, &fl, sizeof (u32)); |
| vec_resize (h.free_lists, fl); |
| |
| for (i = 0; i < vec_len (h.free_lists); i++) |
| vec_unserialize (m, &h.free_lists[i], unserialize_vec_32); |
| |
| vec_unserialize (m, &h.elts, unserialize_vec_heap_elt); |
| vec_unserialize (m, &h.small_free_elt_free_index, unserialize_vec_32); |
| vec_unserialize (m, &h.free_elts, unserialize_vec_32); |
| |
| /* Re-construct used elt bitmap. */ |
| if (CLIB_DEBUG > 0) |
| { |
| heap_elt_t *e; |
| vec_foreach (e, h.elts) |
| { |
| if (!heap_is_free (e)) |
| h.used_elt_bitmap = clib_bitmap_ori (h.used_elt_bitmap, e - h.elts); |
| } |
| } |
| |
| heap = *result = _heap_new (vl, h.elt_bytes); |
| heap_header (heap)[0] = h; |
| |
| /* Unserialize data in heap. */ |
| { |
| heap_elt_t *e, *end; |
| e = h.elts + h.head; |
| end = h.elts + h.tail; |
| while (1) |
| { |
| if (!heap_is_free (e)) |
| { |
| void *v = heap + heap_offset (e) * h.elt_bytes; |
| u32 n = heap_elt_size (heap, e); |
| unserialize (m, f, v, n); |
| } |
| if (e == end) |
| break; |
| e = heap_next (e); |
| } |
| } |
| } |
| |
| void |
| serialize_magic (serialize_main_t * m, void *magic, u32 magic_bytes) |
| { |
| void *p; |
| serialize_integer (m, magic_bytes, sizeof (magic_bytes)); |
| p = serialize_get (m, magic_bytes); |
| clib_memcpy_fast (p, magic, magic_bytes); |
| } |
| |
| void |
| unserialize_check_magic (serialize_main_t * m, void *magic, u32 magic_bytes) |
| { |
| u32 l; |
| void *d; |
| |
| unserialize_integer (m, &l, sizeof (l)); |
| if (l != magic_bytes) |
| { |
| bad: |
| serialize_error_return (m, "bad magic number"); |
| } |
| d = serialize_get (m, magic_bytes); |
| if (memcmp (magic, d, magic_bytes)) |
| goto bad; |
| } |
| |
| __clib_export clib_error_t * |
| va_serialize (serialize_main_t * sm, va_list * va) |
| { |
| serialize_main_header_t *m = &sm->header; |
| serialize_function_t *f = va_arg (*va, serialize_function_t *); |
| clib_error_t *error = 0; |
| |
| m->recursion_level += 1; |
| if (m->recursion_level == 1) |
| { |
| uword r = clib_setjmp (&m->error_longjmp, 0); |
| error = uword_to_pointer (r, clib_error_t *); |
| } |
| |
| if (!error) |
| f (sm, va); |
| |
| m->recursion_level -= 1; |
| return error; |
| } |
| |
| __clib_export clib_error_t * |
| serialize (serialize_main_t * m, ...) |
| { |
| clib_error_t *error; |
| va_list va; |
| |
| va_start (va, m); |
| error = va_serialize (m, &va); |
| va_end (va); |
| return error; |
| } |
| |
| __clib_export clib_error_t * |
| unserialize (serialize_main_t * m, ...) |
| { |
| clib_error_t *error; |
| va_list va; |
| |
| va_start (va, m); |
| error = va_serialize (m, &va); |
| va_end (va); |
| return error; |
| } |
| |
| static void * |
| serialize_write_not_inline (serialize_main_header_t * m, |
| serialize_stream_t * s, |
| uword n_bytes_to_write, uword flags) |
| { |
| uword cur_bi, n_left_b, n_left_o; |
| |
| ASSERT (s->current_buffer_index <= s->n_buffer_bytes); |
| cur_bi = s->current_buffer_index; |
| n_left_b = s->n_buffer_bytes - cur_bi; |
| n_left_o = vec_len (s->overflow_buffer); |
| |
| /* Prepend overflow buffer if present. */ |
| do |
| { |
| if (n_left_o > 0 && n_left_b > 0) |
| { |
| uword n = clib_min (n_left_b, n_left_o); |
| clib_memcpy_fast (s->buffer + cur_bi, s->overflow_buffer, n); |
| cur_bi += n; |
| n_left_b -= n; |
| n_left_o -= n; |
| if (n_left_o == 0) |
| _vec_len (s->overflow_buffer) = 0; |
| else |
| vec_delete (s->overflow_buffer, n, 0); |
| } |
| |
| /* Call data function when buffer is complete. Data function should |
| dispatch with current buffer and give us a new one to write more |
| data into. */ |
| if (n_left_b == 0) |
| { |
| s->current_buffer_index = cur_bi; |
| m->data_function (m, s); |
| cur_bi = s->current_buffer_index; |
| n_left_b = s->n_buffer_bytes - cur_bi; |
| } |
| } |
| while (n_left_o > 0); |
| |
| if (n_left_o > 0 || n_left_b < n_bytes_to_write) |
| { |
| u8 *r; |
| vec_add2 (s->overflow_buffer, r, n_bytes_to_write); |
| return r; |
| } |
| else |
| { |
| s->current_buffer_index = cur_bi + n_bytes_to_write; |
| return s->buffer + cur_bi; |
| } |
| } |
| |
| static void * |
| serialize_read_not_inline (serialize_main_header_t * m, |
| serialize_stream_t * s, |
| uword n_bytes_to_read, uword flags) |
| { |
| uword cur_bi, cur_oi, n_left_b, n_left_o, n_left_to_read; |
| |
| ASSERT (s->current_buffer_index <= s->n_buffer_bytes); |
| |
| cur_bi = s->current_buffer_index; |
| cur_oi = s->current_overflow_index; |
| |
| n_left_b = s->n_buffer_bytes - cur_bi; |
| n_left_o = vec_len (s->overflow_buffer) - cur_oi; |
| |
| /* Read from overflow? */ |
| if (n_left_o >= n_bytes_to_read) |
| { |
| s->current_overflow_index = cur_oi + n_bytes_to_read; |
| return vec_elt_at_index (s->overflow_buffer, cur_oi); |
| } |
| |
| /* Reset overflow buffer. */ |
| if (n_left_o == 0 && s->overflow_buffer) |
| { |
| s->current_overflow_index = 0; |
| _vec_len (s->overflow_buffer) = 0; |
| } |
| |
| n_left_to_read = n_bytes_to_read; |
| while (n_left_to_read > 0) |
| { |
| uword n; |
| |
| /* If we don't have enough data between overflow and normal buffer |
| call read function. */ |
| if (n_left_o + n_left_b < n_bytes_to_read) |
| { |
| /* Save any left over buffer in overflow vector. */ |
| if (n_left_b > 0) |
| { |
| vec_add (s->overflow_buffer, s->buffer + cur_bi, n_left_b); |
| n_left_o += n_left_b; |
| n_left_to_read -= n_left_b; |
| /* Advance buffer to end --- even if |
| SERIALIZE_FLAG_NO_ADVANCE_CURRENT_BUFFER_INDEX is set. */ |
| cur_bi = s->n_buffer_bytes; |
| n_left_b = 0; |
| } |
| |
| if (m->data_function) |
| { |
| m->data_function (m, s); |
| cur_bi = s->current_buffer_index; |
| n_left_b = s->n_buffer_bytes - cur_bi; |
| } |
| } |
| |
| /* For first time through loop return if we have enough data |
| in normal buffer and overflow vector is empty. */ |
| if (n_left_o == 0 |
| && n_left_to_read == n_bytes_to_read && n_left_b >= n_left_to_read) |
| { |
| s->current_buffer_index = cur_bi + n_bytes_to_read; |
| return s->buffer + cur_bi; |
| } |
| |
| if (!m->data_function || serialize_stream_is_end_of_stream (s)) |
| { |
| /* This can happen for a peek at end of file. |
| Pad overflow buffer with 0s. */ |
| vec_resize (s->overflow_buffer, n_left_to_read); |
| n_left_o += n_left_to_read; |
| n_left_to_read = 0; |
| } |
| else |
| { |
| /* Copy from buffer to overflow vector. */ |
| n = clib_min (n_left_to_read, n_left_b); |
| vec_add (s->overflow_buffer, s->buffer + cur_bi, n); |
| cur_bi += n; |
| n_left_b -= n; |
| n_left_o += n; |
| n_left_to_read -= n; |
| } |
| } |
| |
| s->current_buffer_index = cur_bi; |
| s->current_overflow_index = cur_oi + n_bytes_to_read; |
| return vec_elt_at_index (s->overflow_buffer, cur_oi); |
| } |
| |
| __clib_export void * |
| serialize_read_write_not_inline (serialize_main_header_t * m, |
| serialize_stream_t * s, |
| uword n_bytes, uword flags) |
| { |
| return (((flags & SERIALIZE_FLAG_IS_READ) ? serialize_read_not_inline : |
| serialize_write_not_inline) (m, s, n_bytes, flags)); |
| } |
| |
| static void |
| serialize_read_write_close (serialize_main_header_t * m, |
| serialize_stream_t * s, uword flags) |
| { |
| if (serialize_stream_is_end_of_stream (s)) |
| return; |
| |
| if (flags & SERIALIZE_FLAG_IS_WRITE) |
| /* "Write" 0 bytes to flush overflow vector. */ |
| serialize_write_not_inline (m, s, /* n bytes */ 0, flags); |
| |
| serialize_stream_set_end_of_stream (s); |
| |
| /* Call it one last time to flush buffer and close. */ |
| m->data_function (m, s); |
| |
| vec_free (s->overflow_buffer); |
| } |
| |
| __clib_export void |
| serialize_close (serialize_main_t * m) |
| { |
| serialize_read_write_close (&m->header, &m->stream, |
| SERIALIZE_FLAG_IS_WRITE); |
| } |
| |
| __clib_export void |
| unserialize_close (serialize_main_t * m) |
| { |
| serialize_read_write_close (&m->header, &m->stream, SERIALIZE_FLAG_IS_READ); |
| } |
| |
| __clib_export void |
| serialize_open_data (serialize_main_t * m, u8 * data, uword n_data_bytes) |
| { |
| clib_memset (m, 0, sizeof (m[0])); |
| m->stream.buffer = data; |
| m->stream.n_buffer_bytes = n_data_bytes; |
| } |
| |
| __clib_export void |
| unserialize_open_data (serialize_main_t * m, u8 * data, uword n_data_bytes) |
| { |
| serialize_open_data (m, data, n_data_bytes); |
| } |
| |
| static void |
| serialize_vector_write (serialize_main_header_t * m, serialize_stream_t * s) |
| { |
| if (!serialize_stream_is_end_of_stream (s)) |
| { |
| /* Double buffer size. */ |
| uword l = vec_len (s->buffer); |
| vec_resize (s->buffer, l > 0 ? l : 64); |
| s->n_buffer_bytes = vec_len (s->buffer); |
| } |
| } |
| |
| __clib_export void |
| serialize_open_vector (serialize_main_t * m, u8 * vector) |
| { |
| clib_memset (m, 0, sizeof (m[0])); |
| m->header.data_function = serialize_vector_write; |
| m->stream.buffer = vector; |
| m->stream.current_buffer_index = 0; |
| m->stream.n_buffer_bytes = vec_len (vector); |
| } |
| |
| __clib_export void * |
| serialize_close_vector (serialize_main_t * m) |
| { |
| serialize_stream_t *s = &m->stream; |
| void *result; |
| |
| serialize_close (m); /* frees overflow buffer */ |
| |
| if (s->buffer) |
| _vec_len (s->buffer) = s->current_buffer_index; |
| result = s->buffer; |
| clib_memset (m, 0, sizeof (m[0])); |
| return result; |
| } |
| |
| __clib_export void |
| serialize_multiple_1 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| u8 *d = data; |
| u8 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = serialize_get (m, 4 * sizeof (d[0])); |
| p[0] = d[0 * data_stride]; |
| p[1] = d[1 * data_stride]; |
| p[2] = d[2 * data_stride]; |
| p[3] = d[3 * data_stride]; |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = serialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| p[0] = d[0]; |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| __clib_export void |
| serialize_multiple_2 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| void *d = data; |
| u16 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = serialize_get (m, 4 * sizeof (p[0])); |
| clib_mem_unaligned (p + 0, u16) = |
| clib_host_to_net_mem_u16 (d + 0 * data_stride); |
| clib_mem_unaligned (p + 1, u16) = |
| clib_host_to_net_mem_u16 (d + 1 * data_stride); |
| clib_mem_unaligned (p + 2, u16) = |
| clib_host_to_net_mem_u16 (d + 2 * data_stride); |
| clib_mem_unaligned (p + 3, u16) = |
| clib_host_to_net_mem_u16 (d + 3 * data_stride); |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = serialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| clib_mem_unaligned (p + 0, u16) = |
| clib_host_to_net_mem_u16 (d + 0 * data_stride); |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| __clib_export void |
| serialize_multiple_4 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| void *d = data; |
| u32 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = serialize_get (m, 4 * sizeof (p[0])); |
| clib_mem_unaligned (p + 0, u32) = |
| clib_host_to_net_mem_u32 (d + 0 * data_stride); |
| clib_mem_unaligned (p + 1, u32) = |
| clib_host_to_net_mem_u32 (d + 1 * data_stride); |
| clib_mem_unaligned (p + 2, u32) = |
| clib_host_to_net_mem_u32 (d + 2 * data_stride); |
| clib_mem_unaligned (p + 3, u32) = |
| clib_host_to_net_mem_u32 (d + 3 * data_stride); |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = serialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| clib_mem_unaligned (p + 0, u32) = |
| clib_host_to_net_mem_u32 (d + 0 * data_stride); |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| __clib_export void |
| unserialize_multiple_1 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| u8 *d = data; |
| u8 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = unserialize_get (m, 4 * sizeof (d[0])); |
| d[0 * data_stride] = p[0]; |
| d[1 * data_stride] = p[1]; |
| d[2 * data_stride] = p[2]; |
| d[3 * data_stride] = p[3]; |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = unserialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| d[0] = p[0]; |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| __clib_export void |
| unserialize_multiple_2 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| void *d = data; |
| u16 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = unserialize_get (m, 4 * sizeof (p[0])); |
| clib_mem_unaligned (d + 0 * data_stride, u16) = |
| clib_net_to_host_mem_u16 (p + 0); |
| clib_mem_unaligned (d + 1 * data_stride, u16) = |
| clib_net_to_host_mem_u16 (p + 1); |
| clib_mem_unaligned (d + 2 * data_stride, u16) = |
| clib_net_to_host_mem_u16 (p + 2); |
| clib_mem_unaligned (d + 3 * data_stride, u16) = |
| clib_net_to_host_mem_u16 (p + 3); |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = unserialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| clib_mem_unaligned (d + 0 * data_stride, u16) = |
| clib_net_to_host_mem_u16 (p + 0); |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| __clib_export void |
| unserialize_multiple_4 (serialize_main_t *m, void *data, uword data_stride, |
| uword n_data) |
| { |
| void *d = data; |
| u32 *p; |
| uword n_left = n_data; |
| |
| while (n_left >= 4) |
| { |
| p = unserialize_get (m, 4 * sizeof (p[0])); |
| clib_mem_unaligned (d + 0 * data_stride, u32) = |
| clib_net_to_host_mem_u32 (p + 0); |
| clib_mem_unaligned (d + 1 * data_stride, u32) = |
| clib_net_to_host_mem_u32 (p + 1); |
| clib_mem_unaligned (d + 2 * data_stride, u32) = |
| clib_net_to_host_mem_u32 (p + 2); |
| clib_mem_unaligned (d + 3 * data_stride, u32) = |
| clib_net_to_host_mem_u32 (p + 3); |
| n_left -= 4; |
| d += 4 * data_stride; |
| } |
| |
| if (n_left > 0) |
| { |
| p = unserialize_get (m, n_left * sizeof (p[0])); |
| while (n_left > 0) |
| { |
| clib_mem_unaligned (d + 0 * data_stride, u32) = |
| clib_net_to_host_mem_u32 (p + 0); |
| p += 1; |
| d += 1 * data_stride; |
| n_left -= 1; |
| } |
| } |
| } |
| |
| #ifdef CLIB_UNIX |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| static void |
| clib_file_write (serialize_main_header_t * m, serialize_stream_t * s) |
| { |
| int fd, n; |
| |
| fd = s->data_function_opaque; |
| n = write (fd, s->buffer, s->current_buffer_index); |
| if (n < 0) |
| { |
| if (!unix_error_is_fatal (errno)) |
| n = 0; |
| else |
| serialize_error (m, clib_error_return_unix (0, "write")); |
| } |
| if (n == s->current_buffer_index) |
| _vec_len (s->buffer) = 0; |
| else |
| vec_delete (s->buffer, n, 0); |
| s->current_buffer_index = vec_len (s->buffer); |
| } |
| |
| static void |
| clib_file_read (serialize_main_header_t * m, serialize_stream_t * s) |
| { |
| int fd, n; |
| |
| fd = s->data_function_opaque; |
| n = read (fd, s->buffer, vec_len (s->buffer)); |
| if (n < 0) |
| { |
| if (!unix_error_is_fatal (errno)) |
| n = 0; |
| else |
| serialize_error (m, clib_error_return_unix (0, "read")); |
| } |
| else if (n == 0) |
| serialize_stream_set_end_of_stream (s); |
| s->current_buffer_index = 0; |
| s->n_buffer_bytes = n; |
| } |
| |
| static void |
| serialize_open_clib_file_descriptor_helper (serialize_main_t * m, int fd, |
| uword is_read) |
| { |
| clib_memset (m, 0, sizeof (m[0])); |
| vec_resize (m->stream.buffer, 4096); |
| |
| if (!is_read) |
| { |
| m->stream.n_buffer_bytes = vec_len (m->stream.buffer); |
| _vec_len (m->stream.buffer) = 0; |
| } |
| |
| m->header.data_function = is_read ? clib_file_read : clib_file_write; |
| m->stream.data_function_opaque = fd; |
| } |
| |
| __clib_export void |
| serialize_open_clib_file_descriptor (serialize_main_t * m, int fd) |
| { |
| serialize_open_clib_file_descriptor_helper (m, fd, /* is_read */ 0); |
| } |
| |
| __clib_export void |
| unserialize_open_clib_file_descriptor (serialize_main_t * m, int fd) |
| { |
| serialize_open_clib_file_descriptor_helper (m, fd, /* is_read */ 1); |
| } |
| |
| static clib_error_t * |
| serialize_open_clib_file_helper (serialize_main_t * m, char *file, |
| uword is_read) |
| { |
| int fd, mode; |
| |
| mode = is_read ? O_RDONLY : O_RDWR | O_CREAT | O_TRUNC; |
| fd = open (file, mode, 0666); |
| if (fd < 0) |
| return clib_error_return_unix (0, "open `%s'", file); |
| |
| serialize_open_clib_file_descriptor_helper (m, fd, is_read); |
| return 0; |
| } |
| |
| __clib_export clib_error_t * |
| serialize_open_clib_file (serialize_main_t * m, char *file) |
| { |
| return serialize_open_clib_file_helper (m, file, /* is_read */ 0); |
| } |
| |
| __clib_export clib_error_t * |
| unserialize_open_clib_file (serialize_main_t * m, char *file) |
| { |
| return serialize_open_clib_file_helper (m, file, /* is_read */ 1); |
| } |
| |
| #endif /* CLIB_UNIX */ |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |