sc/queue/sc_queue.h
2020-12-28 06:18:18 +03:00

234 lines
7.0 KiB
C

/*
* MIT License
*
* Copyright (c) 2020 Ozan Tezcan
*
* 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.
*/
#ifndef SC_QUEUE_H
#define SC_QUEUE_H
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* Internals, do not use
*/
struct sc_queue
{
size_t cap;
size_t first;
size_t last;
uint8_t elems[];
};
#define sc_queue_meta(q) \
((struct sc_queue *) ((char *) (q) -offsetof(struct sc_queue, elems)))
static inline size_t sc_queue_inc_first(void *q)
{
struct sc_queue *meta = sc_queue_meta(q);
size_t tmp = meta->first;
meta->first = (meta->first + 1) & (meta->cap - 1);
return tmp;
}
static inline size_t sc_queue_inc_last(void *q)
{
struct sc_queue *meta = sc_queue_meta(q);
size_t tmp = meta->last;
meta->last = (meta->last + 1) & (meta->cap - 1);
return tmp;
}
static inline size_t sc_queue_dec_first(void *q)
{
struct sc_queue *meta = sc_queue_meta(q);
meta->first = (meta->first - 1) & (meta->cap - 1);
return meta->first;
}
static inline size_t sc_queue_dec_last(void *q)
{
struct sc_queue *meta = sc_queue_meta(q);
meta->last = (meta->last - 1) & (meta->cap - 1);
return meta->last;
}
bool sc_queue_init(void **q, size_t elem_size, size_t cap);
void sc_queue_term(void **q);
bool sc_queue_expand(void **q, size_t elem_size);
/**
* Plug your allocator if you want.
*/
#define sc_queue_realloc realloc
#define sc_queue_free free
/**
* @param q Queue pointer
* @param count Initial capacity, '0' is a valid value if you don't want to
* allocate memory immediately.
*/
#define sc_queue_create(q, count) \
sc_queue_init((void **) &(q), sizeof(*(q)), count)
/**
* Deallocate underlying memory.
* @param q Queue pointer
*/
#define sc_queue_destroy(q) sc_queue_term(((void **) &(q)))
/**
* @param q Queue pointer
* @return Current capacity
*/
#define sc_queue_cap(q) (sc_queue_meta((q))->cap)
/**
* @param q Queue pointer
* @return Element count
*/
#define sc_queue_size(q) \
((sc_queue_meta(q)->last - sc_queue_meta(q)->first) & \
(sc_queue_meta(q)->cap - 1))
/**
* @param q Queue pointer
* @return true if queue is empty
*/
#define sc_queue_empty(q) ((sc_queue_meta(q)->last == sc_queue_meta(q)->first))
/**
* Clear the queue without deallocating underlying memory.
* @param q Queue pointer
*/
#define sc_queue_clear(q) \
do { \
sc_queue_meta(q)->first = 0; \
sc_queue_meta(q)->last = 0; \
} while (0)
/**
* @param q Queue pointer
* @return Index of the first element. If queue is empty, result is undefined.
*/
#define sc_queue_first(q) (sc_queue_meta(q)->first)
/**
* @param q Queue pointer
* @return Index of the last element. If queue is empty, result is undefined.
*/
#define sc_queue_last(q) (sc_queue_meta(q)->last)
/**
* @return Index of the next element after i, if there is no element after i
* result is undefined.
*/
#define sc_queue_next(q, i) (((i) + 1) & (sc_queue_meta(q)->cap - 1))
/**
* Returns element at 'i'th position, so regular loops are possible :
*
* for (size_t i = 0; i < sc_queue_size(q); i++) {
* printf("%d" \n, sc_queue_at(q, i));
* }
*
* @param q Queue pointer
* @return element at 'i'th position
*/
#define sc_queue_at(q, i) \
(q)[((sc_queue_meta(q)->first) + (i)) & (sc_queue_cap(q) - 1)]
/**
* @param q Queue pointer
* @return First element without removing from the queue.
* If queue is empty, result is undefined
*/
#define sc_queue_peek_first(q) ((q)[sc_queue_meta(q)->first])
/**
* @param q Queue pointer
* @return Last element without removing from the queue.
* If queue is empty, result is undefined
*/
#define sc_queue_peek_last(q) \
(q)[(sc_queue_meta(q)->last - 1) & (sc_queue_meta(q)->cap - 1)]
/**
* @param q Queue pointer
* @param elem Elem to be added at the end of the list
* @return 'true' on success, 'false' on out of memory.
*/
#define sc_queue_add_last(q, elem) \
sc_queue_expand((void **) &(q), sizeof(*(q))) == true ? \
(q)[sc_queue_inc_last((q))] = (elem), \
true : false
/**
* @param q Queue pointer
* @return Delete the last element from the queue and return its value.
* If queue is empty, result is undefined.
*/
#define sc_queue_del_last(q) ((q)[sc_queue_dec_last((q))])
/**
* @param q Queue pointer.
* @param elem Elem to be added at the head of the list.
* @return 'true' on success, 'false' on out of memory.
*/
#define sc_queue_add_first(q, elem) \
sc_queue_expand((void **) &(q), sizeof(*(q))) == true ? \
(q)[sc_queue_dec_first((q))] = (elem), \
true : false
/**
* @param q Queue pointer
* @return Delete the first element from the queue and return its value.
* If queue is empty, result is undefined.
*/
#define sc_queue_del_first(q) (q)[sc_queue_inc_first((q))]
/**
* For each loop,
*
* int *queue;
* sc_queue_create(queue, 4);
*
* int elem;
* sc_queue_foreach(queue, elem) {
* printf("Elem : %d \n, elem);
* }
*/
#define sc_queue_foreach(q, elem) \
for (int _k = 1, _i = sc_queue_first(q); _k && _i != sc_queue_last(q); \
_k = !_k, _i = sc_queue_next(q, _i)) \
for ((elem) = (q)[_i]; _k; _k = !_k)
#endif