diff --git a/Makefile b/Makefile index a4f7588..9b4e110 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ CPPFLAGS += $(shell pkg-config --cflags $(DEPS)) LDLIBS += $(shell pkg-config --libs $(DEPS)) INTERP ?= MAIN = main -OBJS = main.o events.o processing.o graph.o config.o event_code_names.o hash_table.o module_registry.o event_predicate.o nodes/getchar.o nodes/print.o nodes/evdev.o nodes/tee.o nodes/router.o nodes/modifiers.o nodes/modify_predicate.o nodes/uinput.o nodes/assign.o nodes/differentiate.o nodes/scale.o +OBJS = main.o events.o processing.o graph.o config.o event_code_names.o hash_table.o queue.o module_registry.o event_predicate.o nodes/getchar.o nodes/print.o nodes/evdev.o nodes/tee.o nodes/router.o nodes/modifiers.o nodes/modify_predicate.o nodes/uinput.o nodes/assign.o nodes/differentiate.o nodes/scale.o all: $(MAIN) diff --git a/queue.c b/queue.c new file mode 100644 index 0000000..c58c1bd --- /dev/null +++ b/queue.c @@ -0,0 +1,90 @@ +#include +#include +#include "queue.h" + +void +queue_deinit_with_destructor(Queue * q, void (*value_destructor)(QueueValue value, void * destructor_closure), void * destructor_closure) +{ + if (q->values) { + if (value_destructor) { + QUEUE_FOREACH_INDEX(i, q) { + value_destructor(q->values[i], destructor_closure); + } + } + free(q->values); + q->values = NULL; + } + q->capacity = 0; + q->after_tail_idx = 0; + q->head_idx = 0; +} + +static bool +queue_grow(Queue * queue) +{ + size_t old_capacity = queue->capacity; + size_t new_capacity = old_capacity + (old_capacity >> 1) + 2; + QueueValue *values = queue->values; + if (values) { + values = T_REALLOC(values, new_capacity, QueueValue); + if (!values) { + return false; + } + if (queue->head_idx > queue->after_tail_idx) { + size_t old_head_idx = queue->head_idx; + size_t head_shift = new_capacity - old_capacity; + size_t shifted_count = old_capacity - old_head_idx; + QueueValue *old_head_ptr = &values[old_head_idx]; + memmove(old_head_ptr + head_shift, old_head_ptr, shifted_count * sizeof(QueueValue)); + queue->head_idx = old_head_idx + head_shift; + } + } else { + values = T_ALLOC(new_capacity, QueueValue); + if (!values) { + return false; + } + } + queue->values = values; + queue->capacity = new_capacity; + return true; +} + +ssize_t +queue_put(Queue * queue, QueueValue value) +{ + if (queue_is_full(queue)) { + if (!queue_grow(queue)) { + return -1; + } + assert(!queue_is_full(queue)); + } + size_t capacity = queue->capacity; + assert(capacity > 0); + size_t index = queue->after_tail_idx; + queue->values[index] = value; + queue->after_tail_idx = (index + 1) % capacity; + return index; +} + +bool +queue_try_pop(Queue * queue, QueueValue * ret) +{ + if (!queue_try_peek(queue, ret)) { + return false; + } + queue->head_idx = (queue->head_idx + 1) % queue->capacity; + return true; +} + +bool +queue_try_peek(const Queue * queue, QueueValue * ret) +{ + if (queue_is_empty(queue)) { + return false; + } + assert(queue->capacity > 0); + if (ret) { + *ret = queue->values[queue->head_idx]; + } + return true; +} diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..a92ead6 --- /dev/null +++ b/queue.h @@ -0,0 +1,83 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include "defs.h" + +typedef union { + void *as_ptr; + intptr_t as_intptr_t; + intptr_t as_signed; + uintptr_t as_uintptr_t; + uintptr_t as_unsigned; + uint8_t as_bytes[sizeof(void*)]; +} QueueValue; + +#define ZERO_QUEUE_VALUE ((QueueValue) {.as_ptr = NULL}) + +typedef struct { + // Always keep one unused slot to distinguish empty and full queue + size_t capacity; + size_t after_tail_idx; + size_t head_idx; + QueueValue *values; +} Queue; + +#define EMPTY_QUEUE ((Queue) {.capacity = 0, .after_tail_idx = 0, .head_idx = 0, .values = NULL}) + +void queue_deinit_with_destructor(Queue * queue, void (*value_destructor)(QueueValue value, void * destructor_closure), void * destructor_closure); +ssize_t queue_put(Queue * queue, QueueValue value); +bool queue_try_pop(Queue * queue, QueueValue * ret); +bool queue_try_peek(const Queue * queue, QueueValue * ret); + +__attribute__((unused)) inline static size_t +queue_length(const Queue * queue) +{ + ssize_t n = queue->after_tail_idx - queue->head_idx; + if (n < 0) { + n += queue->capacity; + } + return n; +} + +__attribute__((unused)) inline static bool +queue_is_empty(const Queue * queue) +{ + return queue->head_idx == queue->after_tail_idx; +} + +__attribute__((unused)) inline static bool +queue_is_full(const Queue * queue) +{ + size_t capacity = queue->capacity; + if (!capacity) { + return true; + } + return (queue->after_tail_idx + 1) % capacity == queue->head_idx; +} + +__attribute__((unused)) inline static void +queue_deinit(Queue * queue) +{ + queue_deinit_with_destructor(queue, NULL, NULL); +} + +__attribute__((unused)) inline static QueueValue +queue_pop(Queue * queue) +{ + QueueValue value = ZERO_QUEUE_VALUE; + queue_try_pop(queue, &value); + return value; +} + +__attribute__((unused)) inline static QueueValue +queue_peek(const Queue * queue) +{ + QueueValue value = ZERO_QUEUE_VALUE; + queue_try_peek(queue, &value); + return value; +} + +// As long as head and tail indices are correct, division by zero should not happen +#define QUEUE_FOREACH_INDEX(i, q) for (size_t i = (q)->head_idx; i != (q)->after_tail_idx; i = (i + 1) % (q)->capacity) + +#endif /* end of include guard: QUEUE_H_ */