aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clangd1
-rw-r--r--.gitignore3
-rw-r--r--Makefile10
-rw-r--r--compile_commands.json40
-rw-r--r--ls_queue.h167
-rw-r--r--queue.h64
-rw-r--r--tests/tests.c122
7 files changed, 291 insertions, 116 deletions
diff --git a/.clangd b/.clangd
index 29788b8..3f26b1c 100644
--- a/.clangd
+++ b/.clangd
@@ -1,2 +1,3 @@
CompileFlags:
CompilationDatabase: .
+ Add: [-x, c]
diff --git a/.gitignore b/.gitignore
index 4e5193b..80462e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
tests/tests
.cache/
-queue.o
+ls_queue.o
+compile_commands.json
diff --git a/Makefile b/Makefile
index 7dc331d..f145bb2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,11 @@
-tests/tests: queue.o tests/tests.c tests/ls_test.h
- $(CC) -o $@ queue.o tests/tests.c -Itests -I.
+tests/tests: ls_queue.o tests/tests.c tests/ls_test.h
+ $(CC) -o $@ ls_queue.o tests/tests.c -Itests -I.
# Usually you wouldn't do this, but for tests we want this compiled with the
# most pedantic settings.
# Dont use this.
-queue.o: queue.h
- $(CC) -c -x c -o $@ $^ -Wall -Wextra -Wpedantic -Werror -ansi \-std=c89 \
+ls_queue.o: ls_queue.h
+ $(CC) -c -x c -o $@ $^ -Wall -Wextra -Wpedantic -Werror -ansi -std=c89 \
-DLS_QUEUE_IMPLEMENTATION \
-Wno-error=pragma-once-outside-header \
-Wno-pragma-once-outside-header
@@ -14,4 +14,4 @@ queue.o: queue.h
clean:
rm -f tests/tests
- rm -f queue.o
+ rm -f ls_queue.o
diff --git a/compile_commands.json b/compile_commands.json
deleted file mode 100644
index e0ef177..0000000
--- a/compile_commands.json
+++ /dev/null
@@ -1,40 +0,0 @@
-[
- {
- "arguments": [
- "/usr/bin/cc",
- "-c",
- "-x",
- "c",
- "-Wall",
- "-Wextra",
- "-Wpedantic",
- "-Werror",
- "-ansi",
- "-pedantic",
- "-DLS_QUEUE_IMPLEMENTATION",
- "-Wno-error=pragma-once-outside-header",
- "-Wno-pragma-once-outside-header",
- "-o",
- "queue.o",
- "queue.h"
- ],
- "directory": "/home/lion/src/ls/ls_queue",
- "file": "/home/lion/src/ls/ls_queue/queue.h",
- "output": "/home/lion/src/ls/ls_queue/queue.o"
- },
- {
- "arguments": [
- "/usr/bin/cc",
- "-c",
- "queue.o",
- "-Itests",
- "-I.",
- "-o",
- "tests/tests",
- "tests/tests.c"
- ],
- "directory": "/home/lion/src/ls/ls_queue",
- "file": "/home/lion/src/ls/ls_queue/tests/tests.c",
- "output": "/home/lion/src/ls/ls_queue/tests/tests"
- }
-]
diff --git a/ls_queue.h b/ls_queue.h
new file mode 100644
index 0000000..0e5fb8f
--- /dev/null
+++ b/ls_queue.h
@@ -0,0 +1,167 @@
+#pragma once
+
+/* Lion's Standard (LS) type-safe ANSI C queue.
+ *
+ * Version: 1.0
+ * Repo: https://github.com/lionkor/ls_queue
+ * 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_TYPE_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_CAP_DECL(int, int_queue, 32)
+ *
+ * // In one source file:
+ * LS_QUEUE_CAP_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.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+
+#define LS_QUEUE_TYPE_INLINE(T, name, cap) \
+ typedef struct name##_##cap { \
+ T data[(cap) + 1]; \
+ size_t read; \
+ size_t write; \
+ } name; \
+ _ls_QUEUE_TYPE_IMPL_DETAIL(T, name, cap, static inline)
+
+#define LS_QUEUE_TYPE_IMPL(T, name, cap) \
+ _ls_QUEUE_TYPE_IMPL_DETAIL(T, name, cap, )
+
+#define LS_QUEUE_TYPE_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. Use LS_QUEUE_TYPE_INLINE or LS_QUEUE_TYPE_{IMPL,DECL} instead.
+ *
+ * What follows is 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)
+ */
+#define _ls_QUEUE_TYPE_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; \
+ }
diff --git a/queue.h b/queue.h
deleted file mode 100644
index ad4230c..0000000
--- a/queue.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#pragma once
-
-/* Lion's Standard (LS) ANSI C ring buffer queue.
- *
- * Version: 1.0
- * Repo: https://github.com/lionkor/ls_queue
- * SPDX-License-Identifier: MIT
- *
- * ==== TABLE OF CONTENTS ====
- *
- * 1. DESCRIPTION
- * 2. HOW TO USE
- * 3. LICENSE
- *
- * ==== 1. DESCRIPTION ====
- *
- * TODO
- *
- * ==== 2. HOW TO USE ====
- *
- * 1. Copy this file into your project and include it:
- *
- * #include "ls_queue.h"
- *
- * 2. In ONE C file, define LS_QUEUE_IMPLEMENTATION before including:
- *
- * #define LS_QUEUE_IMPLEMENTATION
- * #include "ls_queue.h"
- *
- * 3. TODO
- *
- * TODO
- *
- * ==== 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.
- */
-
-typedef struct ls_queue {
- int a;
-} ls_queue;
diff --git a/tests/tests.c b/tests/tests.c
index 0d57df4..5edb0dc 100644
--- a/tests/tests.c
+++ b/tests/tests.c
@@ -1,14 +1,124 @@
#define LS_TEST_IMPLEMENTATION
#include "ls_test.h"
-int add(int a, int b) {
- return a + b;
+#include "ls_queue.h"
+
+LS_QUEUE_TYPE_INLINE(int, int_queue, 4)
+
+TEST_CASE(queue_init) {
+ int_queue q;
+ int_queue_init(&q);
+ ASSERT_EQ(q.read, 0, "%zu");
+ ASSERT_EQ(q.write, 0, "%zu");
+ return 0;
+}
+
+TEST_CASE(queue_push_single) {
+ int_queue q;
+ int_queue_init(&q);
+
+ int result = int_queue_push(&q, 42);
+ ASSERT_EQ(result, 1, "%d");
+ ASSERT_EQ(q.write, 1, "%zu");
+ ASSERT_EQ(q.read, 0, "%zu");
+
+ return 0;
+}
+
+TEST_CASE(queue_pop_single) {
+ int_queue q;
+ int_queue_init(&q);
+
+ int_queue_push(&q, 42);
+
+ int val;
+ int result = int_queue_pop(&q, &val);
+ ASSERT_EQ(result, 1, "%d");
+ ASSERT_EQ(val, 42, "%d");
+ ASSERT_EQ(q.read, 1, "%zu");
+
+ return 0;
+}
+
+TEST_CASE(queue_pop_empty) {
+ int_queue q;
+ int_queue_init(&q);
+
+ int val;
+ int result = int_queue_pop(&q, &val);
+ ASSERT_EQ(result, 0, "%d");
+
+ return 0;
}
-TEST_CASE(add) {
- ASSERT_EQ(add(1, 2), 3, "%d");
- ASSERT_EQ(add(2, 3), 5, "%d");
- ASSERT_EQ(add(0, 0), 100000, "%d");
+TEST_CASE(queue_fill_to_capacity) {
+ int_queue q;
+ int_queue_init(&q);
+
+ // Fill the queue to capacity (4 elements)
+ ASSERT_EQ(int_queue_push(&q, 1), 1, "%d");
+ ASSERT_EQ(int_queue_push(&q, 2), 1, "%d");
+ ASSERT_EQ(int_queue_push(&q, 3), 1, "%d");
+ ASSERT_EQ(int_queue_push(&q, 4), 1, "%d");
+
+ // Try to push one more (should fail)
+ int result = int_queue_push(&q, 5);
+ ASSERT_EQ(result, 0, "%d");
+
+ return 0;
+}
+
+TEST_CASE(queue_fifo_order) {
+ int_queue q;
+ int_queue_init(&q);
+
+ // Push values in order
+ int_queue_push(&q, 10);
+ int_queue_push(&q, 20);
+ int_queue_push(&q, 30);
+
+ // Pop values and check FIFO order
+ int val;
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 10, "%d");
+
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 20, "%d");
+
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 30, "%d");
+
+ return 0;
+}
+
+TEST_CASE(queue_circular_behavior) {
+ int_queue q;
+ int_queue_init(&q);
+
+ // Fill queue
+ int_queue_push(&q, 1);
+ int_queue_push(&q, 2);
+ int_queue_push(&q, 3);
+ int_queue_push(&q, 4);
+
+ // Pop some elements
+ int val;
+ int_queue_pop(&q, &val);
+ int_queue_pop(&q, &val);
+
+ // Should be able to push again
+ ASSERT_EQ(int_queue_push(&q, 5), 1, "%d");
+ ASSERT_EQ(int_queue_push(&q, 6), 1, "%d");
+
+ // Verify order is maintained
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 3, "%d");
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 4, "%d");
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 5, "%d");
+ int_queue_pop(&q, &val);
+ ASSERT_EQ(val, 6, "%d");
return 0;
}