cons
Loading...
Searching...
No Matches
cons.c
Go to the documentation of this file.
1/* SPDX-License-Identifier: MIT */
2
13#include <cons.h>
14
15/*
16 * for malloc and free
17 */
18#include <stdlib.h>
19
20static bool cons_delete_p(struct cons **list, struct cons *cell, void *user);
21
22static bool cons_remove_p(struct cons **list, struct cons *cell, void *user);
23
24struct cons **cons(struct cons **list, struct cons *cell) {
25 cons_rplacd(cell, *list);
26 *list = cell;
27 return &cell->cdr;
28}
29
30struct cons **cons_prepend(struct cons **list, struct cons *cell) {
31 (void)cons(list, cell);
32 return list;
33}
34
35struct cons **cons_loop(struct cons **list, bool (*pred)(struct cons **list, struct cons *cell, void *user), void *user) {
36 for (struct cons *cell = *list; CONS_NOT_NIL_P(cell); list = &cell->cdr, cell = cons_cdr(cell)) {
37 if (pred(list, cell, user)) {
38 return list;
39 }
40 }
41 /*
42 * Returning NULL is correct. It is a NULL pointer to a pointer to a
43 * cons cell---a pointer to the head of a list. Therefore not itself a
44 * cons cell pointer. Returning NULL indicates that the predicate did
45 * not find a matching cell in the list, and thus there is no pointer
46 * to a cons cell that satisfies the predicate.
47 *
48 * This design allows the function to signal the absence of a matching
49 * cell without returning a pointer to a cons cell, which would be
50 * misleading since it would suggest that a valid cell was found when
51 * in fact it was not. By returning NULL, the function provides a
52 * unambiguous way to indicate that the search was unsuccessful.
53 */
54 return NULL;
55}
56
57static bool cons_find_p(struct cons **list, struct cons *cell, void *user) {
58 (void)list;
59 return cell == user;
60}
61
62struct cons **cons_find(struct cons **list, void *cell) {
63 return cons_loop(list, cons_find_p, cell);
64}
65
66static bool cons_delete_p(struct cons **list, struct cons *cell, void *user) {
67 (void)list;
68 return cons_car(cell) == user;
69}
70
71struct cons *cons_delete(struct cons **list, void *car) {
72 struct cons **found = cons_loop(list, cons_delete_p, car);
73 if (found == NULL) {
74 return CONS_NIL;
75 }
76 struct cons *deleted = *found;
77 *found = cons_cdr(deleted);
78 return deleted;
79}
80
81struct cons *cons_remove(struct cons **list, struct cons *cell) {
82 struct cons **found = cons_find(list, cell);
83 if (found == NULL) {
84 return CONS_NIL;
85 }
86 struct cons *removed = *found;
87 *found = cons_cdr(removed);
88 return removed;
89}
90
91void cons_reverse(struct cons **list) {
92 /*
93 * Maintain a pointer to the reversed list (initially CONS_NIL) and
94 * iteratively traverse the original list. For each cell, save the next cell
95 * (cdr) before reassigning the cdr of the current cell to point to the
96 * reversed list. Then update the reversed list to be the current cell and
97 * move to the next cell in the original list. This process continues until
98 * reversing has traversed the entire original list, at which point the
99 * reversed list will contain all the cells in reverse order.
100 */
101 struct cons *reversed = CONS_NIL, *cell = *list;
102 while (CONS_NOT_NIL_P(cell)) {
103 struct cons *tail = cons_cdr(cell);
104 cons_rplacd(cell, reversed);
105 reversed = cell;
106 cell = tail;
107 }
108 *list = reversed;
109}
110
111size_t cons_length(const struct cons *cell) {
112 size_t length = 0;
113 while (CONS_NOT_NIL_P(cell)) {
114 length++;
115 cell = cons_cdr(cell);
116 }
117 return length;
118}
119
120struct cons *cons_last(struct cons *cell) {
121 if (CONS_NIL_P(cell)) {
122 return CONS_NIL;
123 }
124 /*
125 * The last cell in a list is the one having its cdr field equal to CONS_NIL.
126 */
127 struct cons *tail;
128 while (CONS_NOT_NIL_P(tail = cons_cdr(cell))) {
129 cell = tail;
130 }
131 return cell;
132}
133
134struct cons *cons_nth(struct cons *cell, size_t nth) {
135 while (CONS_NOT_NIL_P(cell) && nth != 0) {
136 cell = cons_cdr(cell);
137 nth--;
138 }
139 return cell;
140}
141
142struct cons *cons_member(struct cons *cell, void *car) {
143 for (; CONS_NOT_NIL_P(cell); cell = cons_cdr(cell)) {
144 if (cons_car(cell) == car) {
145 return cell;
146 }
147 }
148 return CONS_NIL;
149}
150
151struct cons *cons_append(struct cons *cell1, struct cons *cell2) {
152 struct cons *last = cons_last(cell1);
153 if (CONS_NIL_P(last)) {
154 return cell2;
155 }
156 cons_rplacd(last, cell2);
157 return cell1;
158}
159
160struct cons *cons_heap(void *car) {
161 struct cons *cell = (struct cons *)malloc(sizeof(struct cons));
162 if (CONS_NOT_NIL_P(cell)) {
163 cons_init(cell, car);
164 }
165 return cell;
166}
167
168void cons_free(struct cons *cell) {
169 /*
170 * Check for CONS_NIL before freeing the cell, as CONS_NIL is a
171 * special value that represents "the" empty list and should not be
172 * freed. If the cell is not CONS_NIL, assume it is safe to free the
173 * heap-allocated memory for that cell. This check prevents accidental
174 * deallocation of the CONS_NIL sentinel value; although, strictly
175 * speaking, the implementation can free(NULL) without causing
176 * undefined behavior.
177 */
178 if (CONS_NOT_NIL_P(cell)) {
179 free(cell);
180 }
181}
A simple implementation of cons cells for building linked lists.
static struct cons * cons_cdr(const struct cons *cell)
Accessor for the cdr field of a cons cell.
Definition cons.h:104
#define CONS_NIL_P(_cell)
Returns true if the cons cell is CONS_NIL.
Definition cons.h:39
#define CONS_NIL
The empty list (a NULL pointer).
Definition cons.h:36
static void cons_rplacd(struct cons *cell, struct cons *cdr)
Mutator for the cdr field of a cons cell.
Definition cons.h:118
static void * cons_car(const struct cons *cell)
Accessor for the car field of a cons cell.
Definition cons.h:97
#define CONS_NOT_NIL_P(_cell)
Returns true if the cons cell is not CONS_NIL.
Definition cons.h:42
static void cons_init(struct cons *cell, void *car)
Initialises a cons cell with the given car value and cdr set to CONS_NIL.
Definition cons.h:121
struct cons * cons_delete(struct cons **list, void *car)
Destructively deletes the first cons cell with the specified car value from the list.
Definition cons.c:71
void cons_free(struct cons *cell)
Frees a heap-allocated cons cell.
Definition cons.c:168
size_t cons_length(const struct cons *cell)
Computes the length of a linked list of cons cells.
Definition cons.c:111
void cons_reverse(struct cons **list)
Reverses a linked list of cons cells in place.
Definition cons.c:91
struct cons ** cons_find(struct cons **list, void *cell)
Finds the first cons cell in a list that matches a given identity.
Definition cons.c:62
struct cons * cons_append(struct cons *cell1, struct cons *cell2)
Appends one list of cons cells to another.
Definition cons.c:151
struct cons * cons_member(struct cons *cell, void *car)
Returns the first cons cell in a linked list of cons cells whose car field matches the specified valu...
Definition cons.c:142
struct cons ** cons_loop(struct cons **list, bool(*pred)(struct cons **list, struct cons *cell, void *user), void *user)
Loops through a list of cons cells, applying a predicate function to each cell.
Definition cons.c:35
struct cons * cons_last(struct cons *cell)
Returns the last cons cell in a linked list of cons cells.
Definition cons.c:120
struct cons * cons_remove(struct cons **list, struct cons *cell)
Destructively removes the specified cons cell from the list.
Definition cons.c:81
struct cons * cons_heap(void *car)
Allocates a new cons cell on the heap.
Definition cons.c:160
struct cons ** cons_prepend(struct cons **list, struct cons *cell)
Prepends a cons cell to a list, returning the updated list pointer.
Definition cons.c:30
struct cons * cons_nth(struct cons *cell, size_t nth)
Returns the n'th cons cell in a linked list of cons cells.
Definition cons.c:134
Construct cell structure for building linked lists.
Definition cons.h:73
struct cons * cdr
Contents of Decrement Register (CDR).
Definition cons.h:89
void * car
Contents of Address Register (CAR).
Definition cons.h:83