+static inline void
+svm_fifo_size_update (svm_fifo_t * f, svm_fifo_chunk_t * c)
+{
+ svm_fifo_chunk_t *prev;
+ u32 add_bytes = 0;
+
+ if (!c)
+ return;
+
+ f->end_chunk->next = c;
+ while (c)
+ {
+ add_bytes += c->length;
+ prev = c;
+ c = c->next;
+ }
+ f->end_chunk = prev;
+ prev->next = f->start_chunk;
+ f->size += add_bytes;
+ f->nitems = f->size - 1;
+ f->new_chunks = 0;
+}
+
+static void
+svm_fifo_try_size_update (svm_fifo_t * f, u32 new_head)
+{
+ if (new_head % f->size > f->tail % f->size)
+ return;
+
+ svm_fifo_size_update (f, f->new_chunks);
+ f->flags &= ~SVM_FIFO_F_SIZE_UPDATE;
+}
+
+void
+svm_fifo_add_chunk (svm_fifo_t * f, svm_fifo_chunk_t * c)
+{
+ svm_fifo_chunk_t *cur, *prev;
+
+ /* Initialize rbtree if needed and add default chunk to it */
+ if (!(f->flags & SVM_FIFO_F_MULTI_CHUNK))
+ {
+ rb_tree_init (&f->chunk_lookup);
+ rb_tree_add2 (&f->chunk_lookup, 0, pointer_to_uword (f->start_chunk));
+ f->flags |= SVM_FIFO_F_MULTI_CHUNK;
+ }
+
+ /* Initialize chunks and add to lookup rbtree. Expectation is that this is
+ * called with the heap where the rbtree's pool is pushed. */
+ cur = c;
+ if (f->new_chunks)
+ {
+ prev = f->new_chunks;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = c;
+ }
+ else
+ prev = f->end_chunk;
+
+ while (cur)
+ {
+ cur->start_byte = prev->start_byte + prev->length;
+ rb_tree_add2 (&f->chunk_lookup, cur->start_byte,
+ pointer_to_uword (cur));
+ prev = cur;
+ cur = cur->next;
+ }
+
+ /* If fifo is not wrapped, update the size now */
+ if (!svm_fifo_is_wrapped (f))
+ {
+ ASSERT (!f->new_chunks);
+ svm_fifo_size_update (f, c);
+ return;
+ }
+
+ /* Postpone size update */
+ if (!f->new_chunks)
+ {
+ f->new_chunks = c;
+ f->flags |= SVM_FIFO_F_SIZE_UPDATE;
+ }
+}
+
+static inline u8
+svm_fifo_chunk_includes_pos (svm_fifo_chunk_t * c, u32 pos)
+{
+ return (pos >= c->start_byte && pos < c->start_byte + c->length);
+}
+
+/**
+ * Find chunk for given byte position
+ *
+ * @param f fifo
+ * @param pos normalized position in fifo
+ *
+ * @return chunk that includes given position or 0
+ */
+static svm_fifo_chunk_t *
+svm_fifo_find_chunk (svm_fifo_t * f, u32 pos)
+{
+ rb_tree_t *rt = &f->chunk_lookup;
+ rb_node_t *cur, *prev;
+ svm_fifo_chunk_t *c;
+
+ cur = rb_node (rt, rt->root);
+ while (pos != cur->key)
+ {
+ prev = cur;
+ if (pos < cur->key)
+ cur = rb_node_left (rt, cur);
+ else
+ cur = rb_node_right (rt, cur);
+
+ if (rb_node_is_tnil (rt, cur))
+ {
+ /* Hit tnil as a left child. Find predecessor */
+ if (pos < prev->key)
+ {
+ cur = rb_tree_predecessor (rt, prev);
+ c = uword_to_pointer (cur->opaque, svm_fifo_chunk_t *);
+ if (svm_fifo_chunk_includes_pos (c, pos))
+ return c;
+ return 0;
+ }
+ /* Hit tnil as a right child. Check if this is the one, otherwise
+ * search for successor */
+ c = uword_to_pointer (prev->opaque, svm_fifo_chunk_t *);
+ if (svm_fifo_chunk_includes_pos (c, pos))
+ return c;
+
+ cur = rb_tree_successor (rt, prev);
+ c = uword_to_pointer (cur->opaque, svm_fifo_chunk_t *);
+ if (svm_fifo_chunk_includes_pos (c, pos))
+ return c;
+ return 0;
+ }
+ }
+
+ if (!rb_node_is_tnil (rt, cur))
+ return uword_to_pointer (cur->opaque, svm_fifo_chunk_t *);
+ return 0;
+}
+