/* Lion's Standard (LS) type-safe ANSI C queue.
*
* Version: 2.1
* Website: https://libls.org
* GitHub: https://github.com/libls/queue
* Mirror: https://git.libls.org/queue.git
* SPDX-License-Identifier: MIT
*
* ==== TABLE OF CONTENTS ====
*
* 1. DESCRIPTION
* 2. HOW TO USE
* 3. LICENSE
*
* ==== 1. DESCRIPTION ====
*
* A minimal, terse, generic (macro code generated) header-only library in ANSI
* C, which implements a queue.
*
* The implementation does not allocate, and uses a ring-buffer (aka a circular
* buffer) to avoid copying and moving memory.
*
* ==== 2. HOW TO USE ====
*
* Statically sized, type-safe queue.
*
* Use LS_QUEUE_INLINE to generate a static inline version of the library.
* This is the "default" behavior.
*
* If you need a declaration and implementation separately, use
* LS_QUEUE_DECL and make sure to call it with the same arguments as
* LS_QUEUE_IMPL. Put LS_QUEUE_DECL in a header, and LS_QUEUE_IMPL in exactly
* ONE source file.
*
* Simple example:
*
* LS_QUEUE_INLINE(int, int_queue, 32)
*
* // somewhere in the same file
* int_queue q;
* int_queue_init(&q);
* int_queue_push(&q, 42);
* int val;
* if (int_queue_pop(&q, &val)) {
* // do something with val
* }
*
* Alternative example with decl and inline split:
*
* // In your header file:
* LS_QUEUE_DECL(int, int_queue, 32)
*
* // In one source file:
* LS_QUEUE_IMPL(int, int_queue, 32)
*
* // Usage in your code:
* int_queue q;
* int_queue_init(&q);
* if (!int_queue_push(&q, 42)) {
* // handle queue full
* }
* int val;
* if (int_queue_pop(&q, &val)) {
* // do something with val
* }
*
* ==== 3. LICENSE ====
*
* This file is provided under the MIT license. For commercial support and
* maintenance, feel free to use the e-mail below to contact the author(s).
*
* The MIT License (MIT)
*
* Copyright (c) 2026 Lion Kortlepel <[email protected]>
*
* 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.
*/
#pragma once
#include <assert.h>
#include <stddef.h>
#define LS_QUEUE_INLINE(T, name, cap) \
typedef struct name##_##cap { \
T data[(cap) + 1]; \
size_t read; \
size_t write; \
} name; \
_ls_QUEUE_IMPL_DETAIL(T, name, cap, static)
#define LS_QUEUE_IMPL(T, name, cap) \
_ls_QUEUE_IMPL_DETAIL(T, name, cap, )
#define LS_QUEUE_DECL(T, name, cap) \
typedef struct name##_##cap { \
T data[(cap) + 1]; \
size_t read; \
size_t write; \
} name; \
void name##_init(name* q); \
/* Returns 0 if full, 1 if successful. */ \
int name##_push(name* q, T val); \
/* Returns 0 if empty, 1 if successful. */ \
int name##_pop(name* q, T* out);
/* DO NOT USE THE MACRO BELOW. Use LS_QUEUE_INLINE or
* LS_QUEUE_{IMPL,DECL} instead.
*
* What follows are some ramblings about the implementation.
*
* You might notice that the queue struct has two different names, once the
* normal name, e.g. `int_queue`, and once a name with size, `struct
* int_queue_16`. You might further notice that the functions take the sized
* version, not the typedef'd version.
*
* While this might seem odd, its essentially a poor-man's way to ensure that,
* if two different int_queue structs are declared, only the one that is defined
* for will work, and the error should make it painfully clear. For example:
*
* > In included file: typedef redefinition with different types ('struct
* int_queue_18' vs 'struct int_queue_16') (clang
* redefinition_different_typedef)
*
* Here you can see that the sizes differ in the declarations: one was declared
* with 16 elements, the other with 18. However, it's really ugly to use those,
* so the typedefs exist, and the functions are named without the size
* specifier, too.
*/
#define _ls_QUEUE_IMPL_DETAIL(T, name, cap, specifier) \
specifier void name##_init(struct name##_##cap* q) { \
assert(q != NULL); \
q->read = 0; \
q->write = 0; \
} \
/* Returns 0 if full, 1 if successful. */ \
specifier int name##_push(struct name##_##cap* q, T val) { \
assert(q != NULL); \
size_t size = sizeof(q->data) / sizeof(T); \
size_t new_write = (q->write + 1) % size; \
if (new_write == q->read) { \
return 0; \
} \
q->data[q->write] = val; \
q->write = new_write; \
return 1; \
} \
/* Returns 0 if empty, 1 if successful. */ \
specifier int name##_pop(struct name##_##cap* q, T* out) { \
assert(q != NULL); \
assert(out != NULL); \
size_t size = sizeof(q->data) / sizeof(T); \
if (q->read == q->write) { \
return 0; \
} \
*out = q->data[q->read]; \
q->read = (q->read + 1) % size; \
return 1; \
}