mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
2fc507c98f
* modelines: tw -> sw shiftwidth, not textwidth. * space-surround modelines * fix irregular modelines * Fix modeline in titlegen.c
322 lines
12 KiB
C
322 lines
12 KiB
C
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set noet ft=c ts=8 sw=8 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2016 Google Inc. │
|
|
│ │
|
|
│ Licensed under the Apache License, Version 2.0 (the "License"); │
|
|
│ you may not use this file except in compliance with the License. │
|
|
│ You may obtain a copy of the License at │
|
|
│ │
|
|
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
|
│ │
|
|
│ Unless required by applicable law or agreed to in writing, software │
|
|
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
|
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
|
│ See the License for the specific language governing permissions and │
|
|
│ limitations under the License. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/intrin/dll.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "third_party/nsync/array.internal.h"
|
|
#include "third_party/nsync/testing/smprintf.h"
|
|
#include "third_party/nsync/testing/testing.h"
|
|
|
|
/* This tests internal abstractions. */
|
|
|
|
typedef A_TYPE (int) a_int; /* an array of 32-bit integers */
|
|
static const a_int a_int_empty = A_EMPTY; /* the empty array */
|
|
|
|
/* Append the integers in the argument list to *a, until the first negative one is found. */
|
|
static a_int *a_set (a_int *a, ...) {
|
|
va_list ap;
|
|
int x;
|
|
A_SET_LEN (a, 0);
|
|
va_start (ap, a);
|
|
for (x = va_arg (ap, int); x >= 0; x = va_arg (ap, int)) {
|
|
A_PUSH (a) = x;
|
|
}
|
|
va_end (ap);
|
|
return (a);
|
|
}
|
|
|
|
/* Remove the first element from *a. Requires that *a be non-empty. */
|
|
static void a_remove_first (a_int *a) {
|
|
int len = A_LEN (a);
|
|
if (len == 0) {
|
|
*(volatile int *)0 = 0;
|
|
} else {
|
|
memmove (&A (a, 0), &A (a, 1), sizeof (A (a, 0)) * (len - 1));
|
|
A_SET_LEN (a, len-1);
|
|
}
|
|
}
|
|
|
|
|
|
/* Return a malloced, nul-terminated string representation of the elements of *a. */
|
|
static char *a_string (const a_int *a) {
|
|
int m = A_LEN (a) * 3 + 3;
|
|
int n = 0;
|
|
char *buf = (char *) malloc (m);
|
|
char single[32];
|
|
int i;
|
|
snprintf (buf+n, m-n, "[");
|
|
n = strlen (buf);
|
|
for (i = 0; i != A_LEN (a); i++) {
|
|
int len;
|
|
snprintf (single, sizeof (single), "%s%lu", i == 0? "": " ",
|
|
(unsigned long)A (a, i));
|
|
len = strlen (single);
|
|
if (m < n + len + 2) {
|
|
buf = (char *) realloc (buf, m *= 2);
|
|
}
|
|
snprintf (buf + n, m-n, "%s", single);
|
|
n += len;
|
|
}
|
|
snprintf (buf+n, m-n, "]");
|
|
return (buf);
|
|
}
|
|
|
|
/* A list item for use in the tests below */
|
|
struct list_item_s {
|
|
struct Dll e;
|
|
int i;
|
|
};
|
|
/* Return a pointer to the struct list_item_s containing struct Dll *e_. */
|
|
#define LIST_ITEM(e_) DLL_CONTAINER(struct list_item_s, e, e_)
|
|
|
|
|
|
/* Check that list l contains elements containing the values in
|
|
expected, by scanning both forwards and backwards through the list. Also
|
|
verify that dll_first() and dll_last() return the first and last element
|
|
found during those iterations, and that dll_is_empty() yields the right value. */
|
|
static void verify_list (testing t, const char *label, struct Dll *l,
|
|
const a_int *expected, const char *file, int line) {
|
|
struct Dll *first;
|
|
struct Dll *last = NULL;
|
|
struct Dll *p;
|
|
int i = 0;
|
|
char *expected_str = a_string (expected);
|
|
for (p = dll_first (l); p != NULL; p = dll_next (l, p)) {
|
|
if (A (expected, i) != LIST_ITEM (p)->i) {
|
|
TEST_ERROR (t, ("%s:%d; %s:expected=%s: expected %d as "
|
|
"value %d in list, but found %d\n",
|
|
file, line, label, expected_str,
|
|
A (expected, i), i, LIST_ITEM (p)->i));
|
|
}
|
|
last = p;
|
|
i++;
|
|
}
|
|
if (last != dll_last (l)) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected %p as "
|
|
"last item in list, but found %p\n",
|
|
file, line, label, expected_str, last, dll_last (l)));
|
|
}
|
|
if (i != A_LEN (expected)) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected %d items in "
|
|
"list, but found %d\n",
|
|
file, line, label, expected_str, A_LEN (expected), i));
|
|
}
|
|
|
|
first = NULL;
|
|
for (p = dll_last (l); p != NULL; p = dll_prev (l, p)) {
|
|
i--;
|
|
if (A (expected, i) != LIST_ITEM (p)->i) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected %d as "
|
|
"value %d in reverse list, but found %d\n",
|
|
file, line, label, expected_str,
|
|
A (expected, i), i, LIST_ITEM (p)->i));
|
|
}
|
|
first = p;
|
|
}
|
|
if (first != dll_first (l)) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected %p as "
|
|
"first item in list, but found %p\n",
|
|
file, line, label, expected_str, first, dll_last (l)));
|
|
}
|
|
if (i != 0) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected %d items "
|
|
"in reverse list, but found %d\n",
|
|
file, line, label, expected_str,
|
|
A_LEN (expected), A_LEN (expected)-i));
|
|
}
|
|
|
|
if ((A_LEN (expected) == 0) != dll_is_empty (l)) {
|
|
TEST_ERROR (t, ("%s:%d: %s:expected=%s: expected dll_is_empty() "
|
|
"to yield %d but got %d\n",
|
|
file, line, label, expected_str,
|
|
(A_LEN (expected) == 0), dll_is_empty (l)));
|
|
}
|
|
free (expected_str);
|
|
}
|
|
|
|
/* Return a new list containing the count integers from start to
|
|
start+count-1 by appending successive elements to the list.
|
|
This exercises dll_make_last() using singleton elements. */
|
|
static struct Dll *make_list (int start, int count) {
|
|
struct Dll *l = NULL;
|
|
int i;
|
|
for (i = start; i != start+count; i++) {
|
|
struct list_item_s *item =
|
|
(struct list_item_s *) malloc (sizeof (*item));
|
|
dll_init (&item->e);
|
|
item->i = i;
|
|
dll_make_last (&l, &item->e);
|
|
}
|
|
return (l);
|
|
}
|
|
|
|
/* Return a new list containing the count integers from start to
|
|
start+count-1 by prefixing the list with elements, starting with the last.
|
|
It exercises dll_make_first() using singleton elements. */
|
|
static struct Dll *make_rlist (int start, int count) {
|
|
struct Dll *l = NULL;
|
|
int i;
|
|
for (i = start + count - 1; i != start-1; i--) {
|
|
struct list_item_s *item =
|
|
(struct list_item_s *) malloc (sizeof (*item));
|
|
dll_init (&item->e);
|
|
item->i = i;
|
|
dll_make_first (&l, &item->e);
|
|
}
|
|
return (l);
|
|
}
|
|
|
|
/* Test the functionality of the various doubly-linked list
|
|
operations internal to the nsync_mu implementation. */
|
|
static void test_dll (testing t) {
|
|
int i;
|
|
a_int expected;
|
|
struct list_item_s *item;
|
|
|
|
struct Dll *empty = NULL;
|
|
struct Dll *list = NULL;
|
|
|
|
struct Dll *x10 = NULL;
|
|
struct Dll *x20 = NULL;
|
|
struct Dll *x30 = NULL;
|
|
struct Dll *x40 = NULL;
|
|
struct Dll *x50 = NULL;
|
|
|
|
bzero (&expected, sizeof (expected));
|
|
|
|
/* All lists are initially empty. */
|
|
verify_list (t, "empty (0)", empty, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "list (0)", list, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "x10", x10, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "x20", x20, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "x30", x30, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "x40", x40, &a_int_empty, __FILE__, __LINE__);
|
|
verify_list (t, "x50", x50, &a_int_empty, __FILE__, __LINE__);
|
|
|
|
/* Make the xN list have the values N, N+1, N+2. */
|
|
x10 = make_list (10, 3);
|
|
verify_list (t, "x10", x10, a_set (&expected, 10, 11, 12, -1), __FILE__, __LINE__);
|
|
x20 = make_rlist (20, 3);
|
|
verify_list (t, "x20", x20, a_set (&expected, 20, 21, 22, -1), __FILE__, __LINE__);
|
|
x30 = make_list (30, 3);
|
|
verify_list (t, "x30", x30, a_set (&expected, 30, 31, 32, -1), __FILE__, __LINE__);
|
|
x40 = make_list (40, 3);
|
|
verify_list (t, "x40", x40, a_set (&expected, 40, 41, 42, -1), __FILE__, __LINE__);
|
|
x50 = make_list (50, 3);
|
|
verify_list (t, "x50", x50, a_set (&expected, 50, 51, 52, -1), __FILE__, __LINE__);
|
|
|
|
/* Check that adding nothing to an empty list leaves it empty. */
|
|
dll_make_first (&list, NULL);
|
|
verify_list (t, "list(1)", list, &a_int_empty, __FILE__, __LINE__);
|
|
dll_make_first (&list, dll_first (empty));
|
|
verify_list (t, "list(2)", list, &a_int_empty, __FILE__, __LINE__);
|
|
dll_make_first (&list, dll_last (empty));
|
|
verify_list (t, "list(3)", list, &a_int_empty, __FILE__, __LINE__);
|
|
|
|
/* Prefix an empty list with some elements. */
|
|
dll_make_first (&list, dll_first (x10));
|
|
verify_list (t, "list(4)", list, a_set (&expected, 10, 11, 12, -1),
|
|
__FILE__, __LINE__);
|
|
|
|
/* Check that adding nothing no a non-empty list leaves it unchanged. */
|
|
dll_make_first (&list, NULL);
|
|
verify_list (t, "list(5)", list, a_set (&expected, 10, 11, 12, -1),
|
|
__FILE__, __LINE__);
|
|
dll_make_first (&list, dll_first (empty));
|
|
verify_list (t, "list(6)", list, a_set (&expected, 10, 11, 12, -1),
|
|
__FILE__, __LINE__);
|
|
dll_make_first (&list, dll_last (empty));
|
|
verify_list (t, "list(7)", list, a_set (&expected, 10, 11, 12, -1),
|
|
__FILE__, __LINE__);
|
|
|
|
/* Check prefixing the list with some elements. */
|
|
dll_make_first (&list, dll_first (x20));
|
|
verify_list (t, "list(8)", list,
|
|
a_set (&expected, 20, 21, 22, 10, 11, 12, -1),
|
|
__FILE__, __LINE__);
|
|
|
|
/* Check appending elements to list. */
|
|
dll_make_last (&list, dll_last (x30));
|
|
verify_list (t, "list(9)", list,
|
|
a_set (&expected, 20, 21, 22, 10, 11, 12, 30, 31, 32, -1),
|
|
__FILE__, __LINE__);
|
|
|
|
/* Remove the first element. */
|
|
item = (struct list_item_s *) LIST_ITEM (dll_first (list));
|
|
dll_remove (&list, &item->e);
|
|
verify_list (t, "list(10)", list,
|
|
a_set (&expected, 21, 22, 10, 11, 12, 30, 31, 32, -1),
|
|
__FILE__, __LINE__);
|
|
free (item);
|
|
|
|
/* Remove the last element. */
|
|
item = (struct list_item_s *) LIST_ITEM (dll_last (list));
|
|
dll_remove (&list, &item->e);
|
|
verify_list (t, "list(11)", list,
|
|
a_set (&expected, 21, 22, 10, 11, 12, 30, 31, -1),
|
|
__FILE__, __LINE__);
|
|
free (item);
|
|
|
|
/* Remove the third element. */
|
|
item = LIST_ITEM (dll_next (list, dll_next (list, dll_first (list))));
|
|
dll_remove (&list, &item->e);
|
|
verify_list (t, "list(12)",
|
|
list, a_set (&expected, 21, 22, 11, 12, 30, 31, -1),
|
|
__FILE__, __LINE__);
|
|
free (item);
|
|
|
|
/* Remove all elements. */
|
|
a_set (&expected, 21, 22, 11, 12, 30, 31, -1);
|
|
for (i = 0; !dll_is_empty (list); i++) {
|
|
char buf[32];
|
|
item = LIST_ITEM (dll_first (list));
|
|
dll_remove (&list, &item->e);
|
|
a_remove_first (&expected);
|
|
snprintf (buf, sizeof (buf), "list(13.%d)", i);
|
|
verify_list (t, buf, list, &expected, __FILE__, __LINE__);
|
|
free (item);
|
|
}
|
|
verify_list (t, "list(14)", list, &a_int_empty, __FILE__, __LINE__);
|
|
|
|
/* Append some elements to an empty list. */
|
|
dll_make_last (&list, dll_last (x40));
|
|
verify_list (t, "list(15)", list,
|
|
a_set (&expected, 40, 41, 42, -1), __FILE__, __LINE__);
|
|
|
|
/* Use dll_splice_after() to put {50, 51, 52} just after 41, which is
|
|
next (first (list)). */
|
|
dll_splice_after (dll_next (list, dll_first (list)), dll_first (x50));
|
|
verify_list (t, "list(16)", list,
|
|
a_set (&expected, 40, 41, 50, 51, 52, 42, -1),
|
|
__FILE__, __LINE__);
|
|
|
|
A_FREE (&expected);
|
|
|
|
while (!dll_is_empty (list)) {
|
|
item = LIST_ITEM (dll_first (list));
|
|
dll_remove (&list, &item->e);
|
|
free (item);
|
|
}
|
|
}
|
|
|
|
int main (int argc, char *argv[]) {
|
|
testing_base tb = testing_new (argc, argv, 0);
|
|
TEST_RUN (tb, test_dll);
|
|
return (testing_base_exit (tb));
|
|
}
|