260 changes: 260 additions & 0 deletions libc/include/llvm-libc-macros/sys-queue-macros.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
//===-- Macros defined in sys/queue.h header file -------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
#define __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H

#include <llvm-libc-macros/containerof-macro.h>
#include <llvm-libc-macros/null-macro.h>

#ifdef __cplusplus
#define QUEUE_TYPEOF(type) type
#else
#define QUEUE_TYPEOF(type) struct type
#endif

// Singly-linked list definitions.

#define SLIST_HEAD(name, type) \
struct name { \
struct type *first; \
}

#define SLIST_CLASS_HEAD(name, type) \
struct name { \
class type *first; \
}

#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }

#define SLIST_ENTRY(type) \
struct { \
struct type *next; \
}

#define SLIST_CLASS_ENTRY(type) \
struct { \
class type *next; \
}

// Singly-linked list access methods.

#define SLIST_EMPTY(head) ((head)->first == NULL)
#define SLIST_FIRST(head) ((head)->first)
#define SLIST_NEXT(elem, field) ((elem)->field.next)

#define SLIST_FOREACH(var, head, field) \
for ((var) = SLIST_FIRST(head); (var); (var) = SLIST_NEXT(var, field))

#define SLIST_FOREACH_FROM(var, head, field) \
if (!(var)) \
(var) = SLIST_FIRST(head); \
for (; (var); (var) = SLIST_NEXT(var, field))

#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); (var) = (tvar))

#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
if (!(var)) \
(var) = SLIST_FIRST(head); \
for (; (var) && ((tvar) = SLIST_NEXT(var, field), 1); (var) = (tvar))

// Singly-linked list functions.

#define SLIST_CONCAT(head1, head2, type, field) \
do { \
if (SLIST_EMPTY(head1)) { \
if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \
SLIST_INIT(head2); \
} else if (!SLIST_EMPTY(head2)) { \
QUEUE_TYPEOF(type) *cur = SLIST_FIRST(head1); \
while (SLIST_NEXT(cur, field) != NULL) \
cur = SLIST_NEXT(cur, field); \
SLIST_NEXT(cur, field) = SLIST_FIRST(head2); \
SLIST_INIT(head2); \
} \
} while (0)

#define SLIST_INIT(head) \
do { \
SLIST_FIRST(head) = NULL; \
} while (0)

#define SLIST_INSERT_AFTER(slistelem, elem, field) \
do { \
SLIST_NEXT(elem, field) = SLIST_NEXT(slistelem, field); \
SLIST_NEXT(slistelem, field) = (elem); \
} while (0)

#define SLIST_INSERT_HEAD(head, elem, field) \
do { \
SLIST_NEXT(elem, field) = SLIST_FIRST(head); \
SLIST_FIRST(head) = (elem); \
} while (0)

#define SLIST_REMOVE(head, elem, type, field) \
do { \
if (SLIST_FIRST(head) == (elem)) { \
SLIST_REMOVE_HEAD(head, field); \
} else { \
QUEUE_TYPEOF(type) *cur = SLIST_FIRST(head); \
while (SLIST_NEXT(cur, field) != (elem)) \
cur = SLIST_NEXT(cur, field); \
SLIST_REMOVE_AFTER(cur, field); \
} \
} while (0)

#define SLIST_REMOVE_AFTER(elem, field) \
do { \
SLIST_NEXT(elem, field) = SLIST_NEXT(SLIST_NEXT(elem, field), field); \
} while (0)

#define SLIST_REMOVE_HEAD(head, field) \
do { \
SLIST_FIRST(head) = SLIST_NEXT(SLIST_FIRST(head), field); \
} while (0)

#define SLIST_SWAP(head1, head2, type) \
do { \
QUEUE_TYPEOF(type) *first = SLIST_FIRST(head1); \
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
SLIST_FIRST(head2) = first; \
} while (0)

// Singly-linked tail queue definitions.

#define STAILQ_HEAD(name, type) \
struct name { \
struct type *first; \
struct type **last; \
}

#define STAILQ_CLASS_HEAD(name, type) \
struct name { \
class type *first; \
class type **last; \
}

#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).first }

#define STAILQ_ENTRY(type) \
struct { \
struct type *next; \
}

#define STAILQ_CLASS_ENTRY(type) \
struct { \
class type *next; \
}

// Singly-linked tail queue access methods.

#define STAILQ_EMPTY(head) ((head)->first == NULL)
#define STAILQ_FIRST(head) ((head)->first)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY(head) ? NULL : __containerof((head)->last, type, field.next))
#define STAILQ_NEXT(elem, field) ((elem)->field.next)

#define STAILQ_FOREACH(var, head, field) \
for ((var) = STAILQ_FIRST(head); (var); (var) = STAILQ_NEXT(var, field))

#define STAILQ_FOREACH_FROM(var, head, field) \
if (!(var)) \
(var) = STAILQ_FIRST(head); \
for (; (var); (var) = STAILQ_NEXT(var, field))

#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST(head); \
(var) && ((tvar) = STAILQ_NEXT(var, field), 1); (var) = (tvar))

#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
if (!(var)) \
(var) = STAILQ_FIRST(head); \
for (; (var) && ((tvar) = STAILQ_NEXT(var, field), 1); (var) = (tvar))

// Singly-linked tail queue functions.

#define STAILQ_CONCAT(head1, head2, type, field) \
do { \
if (!STAILQ_EMPTY(head2)) { \
*(head1)->last = (head2)->first; \
(head1)->last = (head2)->last; \
STAILQ_INIT(head2); \
} \
} while (0)

#define STAILQ_INIT(head) \
do { \
STAILQ_FIRST(head) = NULL; \
(head)->last = &STAILQ_FIRST(head); \
} while (0)

#define STAILQ_INSERT_AFTER(head, listelem, elem, field) \
do { \
if ((STAILQ_NEXT(elem, field) = STAILQ_NEXT(listelem, field)) == NULL) \
(head)->last = &STAILQ_NEXT(elem, field); \
STAILQ_NEXT(listelem, field) = (elem); \
} while (0)

#define STAILQ_INSERT_HEAD(head, elem, field) \
do { \
if ((STAILQ_NEXT(elem, field) = STAILQ_FIRST(head)) == NULL) \
(head)->last = &STAILQ_NEXT(elem, field); \
STAILQ_FIRST(head) = (elem); \
} while (0)

#define STAILQ_INSERT_TAIL(head, elem, field) \
do { \
STAILQ_NEXT(elem, field) = NULL; \
*(head)->last = (elem); \
(head)->last = &STAILQ_NEXT(elem, field); \
} while (0)

#define STAILQ_REMOVE(head, elem, type, field) \
do { \
if (STAILQ_FIRST(head) == (elem)) { \
STAILQ_REMOVE_HEAD(head, field); \
} else { \
QUEUE_TYPEOF(type) *cur = STAILQ_FIRST(head); \
while (STAILQ_NEXT(cur, field) != (elem)) \
cur = STAILQ_NEXT(cur, field); \
STAILQ_REMOVE_AFTER(head, cur, field); \
} \
} while (0)

#define STAILQ_REMOVE_AFTER(head, elem, field) \
do { \
if ((STAILQ_NEXT(elem, field) = \
STAILQ_NEXT(STAILQ_NEXT(elem, field), field)) == NULL) \
(head)->last = &STAILQ_NEXT(elem, field); \
} while (0)

#define STAILQ_REMOVE_HEAD(head, field) \
do { \
if ((STAILQ_FIRST(head) = STAILQ_NEXT(STAILQ_FIRST(head), field)) == NULL) \
(head)->last = &STAILQ_FIRST(head); \
} while (0)

#define STAILQ_SWAP(head1, head2, type) \
do { \
QUEUE_TYPEOF(type) *first = STAILQ_FIRST(head1); \
QUEUE_TYPEOF(type) **last = (head1)->last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
(head1)->last = (head2)->last; \
STAILQ_FIRST(head2) = first; \
(head2)->last = last; \
if (STAILQ_EMPTY(head1)) \
(head1)->last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
(head2)->last = &STAILQ_FIRST(head2); \
} while (0)

#endif // __LLVM_LIBC_MACROS_SYS_QUEUE_MACROS_H
14 changes: 14 additions & 0 deletions libc/include/sys/queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//===-- BSD sys/queue.h ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SYS_QUEUE_H
#define LLVM_LIBC_SYS_QUEUE_H

#include <llvm-libc-macros/sys-queue-macros.h>

#endif // LLVM_LIBC_SYS_QUEUE_H
1 change: 1 addition & 0 deletions libc/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND
return()
endif()

add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(utils)

Expand Down
16 changes: 16 additions & 0 deletions libc/test/include/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_custom_target(libc_include_tests)

add_libc_unittest(
sys_queue_test
SUITE
libc_include_tests
SRCS
sys/queue_test.cpp
DEPENDS
libc.include.llvm-libc-macros.sys_queue_macros
libc.src.__support.char_vector
libc.src.__support.CPP.string
COMPILE_OPTIONS
# This is needed because the __containerof macro uses statement expression.
-Wno-gnu-statement-expression-from-macro-expansion
)
168 changes: 168 additions & 0 deletions libc/test/include/sys/queue_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//===-- Unittests for queue -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDSList-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/string.h"
#include "src/__support/char_vector.h"
#include "test/UnitTest/Test.h"

#include "llvm-libc-macros/sys-queue-macros.h"

using LIBC_NAMESPACE::CharVector;
using LIBC_NAMESPACE::cpp::string;

namespace LIBC_NAMESPACE {

TEST(LlvmLibcQueueTest, SList) {
struct Entry {
char c;
SLIST_ENTRY(Entry) entries;
};

SLIST_HEAD(Head, Entry);

Head head = SLIST_HEAD_INITIALIZER(head);

struct Contains : public testing::Matcher<Head> {
string s;
Contains(string s) : s(s) {}
bool match(Head head) {
Entry *e;
CharVector v;
SLIST_FOREACH(e, &head, entries) { v.append(e->c); }
return s == v.c_str();
}
};

Entry e1 = {'a', {NULL}};
SLIST_INSERT_HEAD(&head, &e1, entries);

ASSERT_THAT(head, Contains("a"));

Entry e2 = {'b', {NULL}};
SLIST_INSERT_AFTER(&e1, &e2, entries);

ASSERT_THAT(head, Contains("ab"));

Head head2 = SLIST_HEAD_INITIALIZER(head);

Entry e3 = {'c', {NULL}};
SLIST_INSERT_HEAD(&head2, &e3, entries);

ASSERT_THAT(head2, Contains("c"));

SLIST_SWAP(&head, &head2, Entry);

ASSERT_THAT(head2, Contains("ab"));

SLIST_CONCAT(&head2, &head, Entry, entries);

ASSERT_THAT(head2, Contains("abc"));

SLIST_CONCAT(&head, &head2, Entry, entries);

ASSERT_THAT(head, Contains("abc"));

Entry *e = NULL, *tmp = NULL;
SLIST_FOREACH_SAFE(e, &head, entries, tmp) {
if (e == &e2) {
SLIST_REMOVE(&head, e, Entry, entries);
}
}

ASSERT_THAT(head, Contains("ac"));

while (!SLIST_EMPTY(&head)) {
e = SLIST_FIRST(&head);
SLIST_REMOVE_HEAD(&head, entries);
}

ASSERT_TRUE(SLIST_EMPTY(&head));
}

TEST(LlvmLibcQueueTest, STailQ) {
struct Entry {
char c;
STAILQ_ENTRY(Entry) entries;
};

STAILQ_HEAD(Head, Entry);

Head head = STAILQ_HEAD_INITIALIZER(head);

struct Contains : public testing::Matcher<Head> {
string s;
Contains(string s) : s(s) {}
bool match(Head head) {
Entry *e;
CharVector v;
STAILQ_FOREACH(e, &head, entries) { v.append(e->c); }
return s == v.c_str();
}
};

STAILQ_INIT(&head);
ASSERT_TRUE(STAILQ_EMPTY(&head));

Entry e1 = {'a', {NULL}};
STAILQ_INSERT_HEAD(&head, &e1, entries);

ASSERT_THAT(head, Contains("a"));

Entry e2 = {'b', {NULL}};
STAILQ_INSERT_TAIL(&head, &e2, entries);

ASSERT_THAT(head, Contains("ab"));

Entry e3 = {'c', {NULL}};
STAILQ_INSERT_AFTER(&head, &e2, &e3, entries);

ASSERT_THAT(head, Contains("abc"));

Head head2 = STAILQ_HEAD_INITIALIZER(head);

Entry e4 = {'d', {NULL}};
STAILQ_INSERT_HEAD(&head2, &e4, entries);

ASSERT_THAT(head2, Contains("d"));

STAILQ_SWAP(&head, &head2, Entry);

ASSERT_THAT(head2, Contains("abc"));

STAILQ_CONCAT(&head2, &head, Entry, entries);

ASSERT_EQ(STAILQ_FIRST(&head2), &e1);
ASSERT_EQ(STAILQ_LAST(&head2, Entry, entries), &e4);

ASSERT_THAT(head2, Contains("abcd"));

STAILQ_CONCAT(&head, &head2, Entry, entries);

ASSERT_EQ(STAILQ_FIRST(&head), &e1);
ASSERT_EQ(STAILQ_LAST(&head, Entry, entries), &e4);

ASSERT_THAT(head, Contains("abcd"));

Entry *e = NULL, *tmp = NULL;
STAILQ_FOREACH_SAFE(e, &head, entries, tmp) {
if (e == &e2) {
STAILQ_REMOVE(&head, e, Entry, entries);
}
}

ASSERT_THAT(head, Contains("acd"));

while (!STAILQ_EMPTY(&head)) {
e = STAILQ_FIRST(&head);
STAILQ_REMOVE_HEAD(&head, entries);
}

ASSERT_TRUE(STAILQ_EMPTY(&head));
}

} // namespace LIBC_NAMESPACE