aboutsummaryrefslogtreecommitdiffstats
path: root/src/vppinfra/serialize.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vppinfra/serialize.c')
-rw-r--r--src/vppinfra/serialize.c1254
1 files changed, 1254 insertions, 0 deletions
diff --git a/src/vppinfra/serialize.c b/src/vppinfra/serialize.c
new file mode 100644
index 00000000..5d401a08
--- /dev/null
+++ b/src/vppinfra/serialize.c
@@ -0,0 +1,1254 @@
+/*
+ * 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;
+}
+
+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 (p, s, len);
+ }
+}
+
+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 (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 (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 (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
+
+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_resize (0, l, (uword) l * elt_bytes, header_bytes,
+ /* align */ align);
+
+ 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);
+}
+
+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_resize (0, l, (uword) l * elt_bytes, sizeof (p[0]), align);
+ 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;
+ }
+
+ 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 (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_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_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_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 (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);
+}
+
+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);
+}
+
+void
+serialize_close (serialize_main_t * m)
+{
+ serialize_read_write_close (&m->header, &m->stream,
+ SERIALIZE_FLAG_IS_WRITE);
+}
+
+void
+unserialize_close (serialize_main_t * m)
+{
+ serialize_read_write_close (&m->header, &m->stream, SERIALIZE_FLAG_IS_READ);
+}
+
+void
+serialize_open_data (serialize_main_t * m, u8 * data, uword n_data_bytes)
+{
+ memset (m, 0, sizeof (m[0]));
+ m->stream.buffer = data;
+ m->stream.n_buffer_bytes = n_data_bytes;
+}
+
+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);
+ }
+}
+
+void
+serialize_open_vector (serialize_main_t * m, u8 * vector)
+{
+ 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);
+}
+
+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;
+ memset (m, 0, sizeof (m[0]));
+ return result;
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+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
+unix_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
+unix_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_unix_file_descriptor_helper (serialize_main_t * m, int fd,
+ uword is_read)
+{
+ 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 ? unix_file_read : unix_file_write;
+ m->stream.data_function_opaque = fd;
+}
+
+void
+serialize_open_unix_file_descriptor (serialize_main_t * m, int fd)
+{
+ serialize_open_unix_file_descriptor_helper (m, fd, /* is_read */ 0);
+}
+
+void
+unserialize_open_unix_file_descriptor (serialize_main_t * m, int fd)
+{
+ serialize_open_unix_file_descriptor_helper (m, fd, /* is_read */ 1);
+}
+
+static clib_error_t *
+serialize_open_unix_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_unix_file_descriptor_helper (m, fd, is_read);
+ return 0;
+}
+
+clib_error_t *
+serialize_open_unix_file (serialize_main_t * m, char *file)
+{
+ return serialize_open_unix_file_helper (m, file, /* is_read */ 0);
+}
+
+clib_error_t *
+unserialize_open_unix_file (serialize_main_t * m, char *file)
+{
+ return serialize_open_unix_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:
+ */