| /* Copyright 2012 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Queue data structure implementation. |
| */ |
| |
| #include "builtin/assert.h" |
| #include "console.h" |
| #include "queue.h" |
| #include "util.h" |
| |
| #define CPRINTS(format, args...) cprints(CC_MOTION_SENSE, format, ##args) |
| |
| static void queue_action_null(struct queue_policy const *policy, size_t count) |
| { |
| } |
| |
| struct queue_policy const queue_policy_null = { |
| .add = queue_action_null, |
| .remove = queue_action_null, |
| }; |
| |
| void queue_init(struct queue const *q) |
| { |
| ASSERT(q->policy); |
| ASSERT(q->policy->add); |
| ASSERT(q->policy->remove); |
| |
| q->state->head = 0; |
| q->state->tail = 0; |
| q->state->flags = 0; |
| } |
| |
| void queue_enable_buffered_mode(struct queue const *q) |
| { |
| q->state->flags |= QUEUE_BUFFERED_MODE; |
| } |
| |
| int queue_is_empty(struct queue const *q) |
| { |
| return q->state->head == q->state->tail; |
| } |
| |
| size_t queue_count(struct queue const *q) |
| { |
| return q->state->tail - q->state->head; |
| } |
| |
| size_t queue_space(struct queue const *q) |
| { |
| return q->buffer_units - queue_count(q); |
| } |
| |
| int queue_is_full(struct queue const *q) |
| { |
| return (queue_space(q) == 0); |
| } |
| |
| /* |
| * These pictures make the logic below clearer. The H and T markers are the |
| * head and tail indicies after they have been modded by the queue size. The |
| * Empty and Full states are disambiguated by looking at the pre-modded |
| * indicies. |
| * |
| * Empty: T |
| * T == H H |
| * |----------------| |
| * |
| * Normal: H T |
| * H < T |---******-------| |
| * |
| * Wrapped: T H |
| * T < H |***----------***| |
| * |
| * Full: T |
| * T == H H |
| * |****************| |
| */ |
| |
| struct queue_chunk queue_get_write_chunk(struct queue const *q, size_t offset) |
| { |
| size_t head = q->state->head & q->buffer_units_mask; |
| size_t tail = (q->state->tail + offset) & q->buffer_units_mask; |
| size_t last = (tail < head) ? head : /* Wrapped */ |
| q->buffer_units; /* Normal | Empty */ |
| |
| /* Make sure that the offset doesn't exceed free space. */ |
| if (queue_space(q) <= offset) |
| return ((struct queue_chunk){ |
| .count = 0, |
| .buffer = NULL, |
| }); |
| |
| return ((struct queue_chunk){ |
| .count = last - tail, |
| .buffer = q->buffer + (tail * q->unit_bytes), |
| }); |
| } |
| |
| struct queue_chunk queue_get_read_chunk(struct queue const *q) |
| { |
| size_t head = q->state->head & q->buffer_units_mask; |
| size_t tail = q->state->tail & q->buffer_units_mask; |
| size_t last = (queue_is_empty(q) ? head : /* Empty */ |
| ((head < tail) ? tail : /* Normal */ |
| q->buffer_units)); /* Wrapped | Full */ |
| |
| return ((struct queue_chunk){ |
| .count = (last - head), |
| .buffer = q->buffer + (head * q->unit_bytes), |
| }); |
| } |
| |
| size_t queue_advance_head(struct queue const *q, size_t count) |
| { |
| size_t transfer = MIN(count, queue_count(q)); |
| |
| q->state->head += transfer; |
| |
| q->policy->remove(q->policy, transfer); |
| |
| return transfer; |
| } |
| |
| size_t queue_advance_tail(struct queue const *q, size_t count) |
| { |
| size_t transfer = MIN(count, queue_space(q)); |
| |
| if (transfer > 0) { |
| q->state->tail += transfer; |
| q->policy->add(q->policy, transfer); |
| } /* LCOV_EXCL_LINE */ |
| |
| return transfer; |
| } |
| |
| size_t queue_add_unit(struct queue const *q, const void *src) |
| { |
| size_t tail = q->state->tail & q->buffer_units_mask; |
| |
| if (queue_space(q) == 0) |
| return 0; |
| |
| if (q->unit_bytes == 1) |
| q->buffer[tail] = *((uint8_t *)src); |
| else |
| memcpy(q->buffer + tail * q->unit_bytes, src, q->unit_bytes); |
| |
| return queue_advance_tail(q, 1); |
| } |
| |
| size_t queue_add_units(struct queue const *q, const void *src, size_t count) |
| { |
| return queue_add_memcpy(q, src, count, memcpy); |
| } |
| |
| size_t queue_add_memcpy(struct queue const *q, const void *src, size_t count, |
| void *(*memcpy)(void *dest, const void *src, size_t n)) |
| { |
| size_t transfer = MIN(count, queue_space(q)); |
| size_t tail = q->state->tail & q->buffer_units_mask; |
| size_t first = MIN(transfer, q->buffer_units - tail); |
| |
| memcpy(q->buffer + tail * q->unit_bytes, src, first * q->unit_bytes); |
| |
| if (first < transfer) |
| memcpy(q->buffer, |
| ((uint8_t const *)src) + first * q->unit_bytes, |
| (transfer - first) * q->unit_bytes); |
| |
| return queue_advance_tail(q, transfer); |
| } |
| |
| void queue_flush(struct queue const *q) |
| { |
| if (!(q->state->flags & QUEUE_BUFFERED_MODE)) { |
| /* Flushing of a non-buffered queue is a no-op. */ |
| return; /* LCOV_EXCL_LINE */ |
| } |
| |
| /* |
| * Consumers will be notified of a request to flush by getting a call of |
| * consumer_ops.written() with a zero length. (See details in |
| * "consumer.h".) |
| */ |
| q->policy->add(q->policy, 0); |
| } /* LCOV_EXCL_LINE */ |
| |
| static void |
| queue_read_safe(struct queue const *q, void *dest, size_t head, size_t transfer, |
| void *(*memcpy)(void *dest, const void *src, size_t n)) |
| { |
| size_t first = MIN(transfer, q->buffer_units - head); |
| |
| memcpy(dest, q->buffer + head * q->unit_bytes, first * q->unit_bytes); |
| |
| if (first < transfer) |
| memcpy(((uint8_t *)dest) + first * q->unit_bytes, q->buffer, |
| (transfer - first) * q->unit_bytes); |
| } |
| |
| size_t queue_remove_unit(struct queue const *q, void *dest) |
| { |
| size_t head = q->state->head & q->buffer_units_mask; |
| |
| if (queue_count(q) == 0) |
| return 0; |
| |
| if (q->unit_bytes == 1) |
| *((uint8_t *)dest) = q->buffer[head]; |
| else |
| memcpy(dest, q->buffer + head * q->unit_bytes, q->unit_bytes); |
| |
| return queue_advance_head(q, 1); |
| } |
| |
| size_t queue_remove_units(struct queue const *q, void *dest, size_t count) |
| { |
| return queue_remove_memcpy(q, dest, count, memcpy); |
| } |
| |
| size_t queue_remove_memcpy(struct queue const *q, void *dest, size_t count, |
| void *(*memcpy)(void *dest, const void *src, |
| size_t n)) |
| { |
| size_t transfer = MIN(count, queue_count(q)); |
| size_t head = q->state->head & q->buffer_units_mask; |
| |
| queue_read_safe(q, dest, head, transfer, memcpy); |
| |
| return queue_advance_head(q, transfer); |
| } |
| |
| size_t queue_peek_units(struct queue const *q, void *dest, size_t i, |
| size_t count) |
| { |
| return queue_peek_memcpy(q, dest, i, count, memcpy); |
| } |
| |
| size_t queue_peek_memcpy(struct queue const *q, void *dest, size_t i, |
| size_t count, |
| void *(*memcpy)(void *dest, const void *src, size_t n)) |
| { |
| size_t available = queue_count(q); |
| size_t transfer = MIN(count, available - i); |
| |
| if (i < available) { |
| size_t head = (q->state->head + i) & q->buffer_units_mask; |
| |
| queue_read_safe(q, dest, head, transfer, memcpy); |
| } |
| |
| return transfer; |
| } |
| |
| void queue_begin(struct queue const *q, struct queue_iterator *it) |
| { |
| if (queue_is_empty(q)) |
| it->ptr = NULL; |
| else |
| it->ptr = q->buffer + (q->state->head & q->buffer_units_mask) * |
| q->unit_bytes; |
| it->_state.offset = 0; |
| it->_state.head = q->state->head; |
| it->_state.tail = q->state->tail; |
| } |
| |
| void queue_next(struct queue const *q, struct queue_iterator *it) |
| { |
| uint8_t *ptr = (uint8_t *)it->ptr; |
| |
| /* Check if anything changed since the iterator was created. */ |
| if (it->_state.head != q->state->head || |
| it->_state.tail != q->state->tail) { |
| CPRINTS("Concurrent modification error, queue has changed while" |
| " iterating. The iterator is now invalid."); |
| it->ptr = NULL; |
| return; |
| } |
| |
| /* Check if iterator is already at end. */ |
| if (ptr == NULL || |
| it->_state.head + it->_state.offset == it->_state.tail) |
| return; |
| |
| it->_state.offset++; |
| /* Check if we've reached the end. */ |
| if (it->_state.head + it->_state.offset == it->_state.tail) { |
| it->ptr = NULL; |
| return; |
| } |
| |
| ptr = q->buffer + |
| (((it->_state.head + it->_state.offset) & q->buffer_units_mask) * |
| q->unit_bytes); |
| it->ptr = (void *)ptr; |
| } |