summaryrefslogtreecommitdiff
path: root/device_mapper/datastruct
diff options
context:
space:
mode:
authorJoe Thornber <ejt@redhat.com>2018-05-14 12:16:43 +0100
committerJoe Thornber <ejt@redhat.com>2018-05-16 13:00:50 +0100
commitccc35e2647b3b78f2fb0d62c25f21fcccbf58950 (patch)
treeb0fc56a0f74b6c6b17c0eeaffa00a921d704b8ec /device_mapper/datastruct
parent7f97c7ea9ade36bab817aca9eeee5c5177550127 (diff)
downloadlvm2-ccc35e2647b3b78f2fb0d62c25f21fcccbf58950.tar.gz
device-mapper: Fork libdm internally.
The device-mapper directory now holds a copy of libdm source. At the moment this code is identical to libdm. Over time code will migrate out to appropriate places (see doc/refactoring.txt). The libdm directory still exists, and contains the source for the libdevmapper shared library, which we will continue to ship (though not neccessarily update). All code using libdm should now use the version in device-mapper.
Diffstat (limited to 'device_mapper/datastruct')
-rw-r--r--device_mapper/datastruct/bitset.c258
-rw-r--r--device_mapper/datastruct/hash.c392
-rw-r--r--device_mapper/datastruct/list.c168
3 files changed, 818 insertions, 0 deletions
diff --git a/device_mapper/datastruct/bitset.c b/device_mapper/datastruct/bitset.c
new file mode 100644
index 000000000..6ae99d3de
--- /dev/null
+++ b/device_mapper/datastruct/bitset.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "misc/dmlib.h"
+
+#include <ctype.h>
+
+/* FIXME: calculate this. */
+#define INT_SHIFT 5
+
+dm_bitset_t dm_bitset_create(struct dm_pool *mem, unsigned num_bits)
+{
+ unsigned n = (num_bits / DM_BITS_PER_INT) + 2;
+ size_t size = sizeof(int) * n;
+ dm_bitset_t bs;
+
+ if (mem)
+ bs = dm_pool_zalloc(mem, size);
+ else
+ bs = dm_zalloc(size);
+
+ if (!bs)
+ return NULL;
+
+ *bs = num_bits;
+
+ return bs;
+}
+
+void dm_bitset_destroy(dm_bitset_t bs)
+{
+ dm_free(bs);
+}
+
+int dm_bitset_equal(dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ if (in1[i] != in2[i])
+ return 0;
+
+ return 1;
+}
+
+void dm_bit_and(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ out[i] = in1[i] & in2[i];
+}
+void dm_bit_union(dm_bitset_t out, dm_bitset_t in1, dm_bitset_t in2)
+{
+ int i;
+ for (i = (in1[0] / DM_BITS_PER_INT) + 1; i; i--)
+ out[i] = in1[i] | in2[i];
+}
+
+static int _test_word(uint32_t test, int bit)
+{
+ uint32_t tb = test >> bit;
+
+ return (tb ? ffs(tb) + bit - 1 : -1);
+}
+
+static int _test_word_rev(uint32_t test, int bit)
+{
+ uint32_t tb = test << (DM_BITS_PER_INT - 1 - bit);
+
+ return (tb ? bit - clz(tb) : -1);
+}
+
+int dm_bit_get_next(dm_bitset_t bs, int last_bit)
+{
+ int bit, word;
+ uint32_t test;
+
+ last_bit++; /* otherwise we'll return the same bit again */
+
+ /*
+ * bs[0] holds number of bits
+ */
+ while (last_bit < (int) bs[0]) {
+ word = last_bit >> INT_SHIFT;
+ test = bs[word + 1];
+ bit = last_bit & (DM_BITS_PER_INT - 1);
+
+ if ((bit = _test_word(test, bit)) >= 0)
+ return (word * DM_BITS_PER_INT) + bit;
+
+ last_bit = last_bit - (last_bit & (DM_BITS_PER_INT - 1)) +
+ DM_BITS_PER_INT;
+ }
+
+ return -1;
+}
+
+int dm_bit_get_prev(dm_bitset_t bs, int last_bit)
+{
+ int bit, word;
+ uint32_t test;
+
+ last_bit--; /* otherwise we'll return the same bit again */
+
+ /*
+ * bs[0] holds number of bits
+ */
+ while (last_bit >= 0) {
+ word = last_bit >> INT_SHIFT;
+ test = bs[word + 1];
+ bit = last_bit & (DM_BITS_PER_INT - 1);
+
+ if ((bit = _test_word_rev(test, bit)) >= 0)
+ return (word * DM_BITS_PER_INT) + bit;
+
+ last_bit = (last_bit & ~(DM_BITS_PER_INT - 1)) - 1;
+ }
+
+ return -1;
+}
+
+int dm_bit_get_first(dm_bitset_t bs)
+{
+ return dm_bit_get_next(bs, -1);
+}
+
+int dm_bit_get_last(dm_bitset_t bs)
+{
+ return dm_bit_get_prev(bs, bs[0] + 1);
+}
+
+/*
+ * Based on the Linux kernel __bitmap_parselist from lib/bitmap.c
+ */
+dm_bitset_t dm_bitset_parse_list(const char *str, struct dm_pool *mem,
+ size_t min_num_bits)
+{
+ unsigned a, b;
+ int c, old_c, totaldigits, ndigits, nmaskbits;
+ int at_start, in_range;
+ dm_bitset_t mask = NULL;
+ const char *start = str;
+ size_t len;
+
+scan:
+ len = strlen(str);
+ totaldigits = c = 0;
+ nmaskbits = 0;
+ do {
+ at_start = 1;
+ in_range = 0;
+ a = b = 0;
+ ndigits = totaldigits;
+
+ /* Get the next value or range of values */
+ while (len) {
+ old_c = c;
+ c = *str++;
+ len--;
+ if (isspace(c))
+ continue;
+
+ /* A '\0' or a ',' signal the end of a value or range */
+ if (c == '\0' || c == ',')
+ break;
+ /*
+ * whitespaces between digits are not allowed,
+ * but it's ok if whitespaces are on head or tail.
+ * when old_c is whilespace,
+ * if totaldigits == ndigits, whitespace is on head.
+ * if whitespace is on tail, it should not run here.
+ * as c was ',' or '\0',
+ * the last code line has broken the current loop.
+ */
+ if ((totaldigits != ndigits) && isspace(old_c))
+ goto_bad;
+
+ if (c == '-') {
+ if (at_start || in_range)
+ goto_bad;
+ b = 0;
+ in_range = 1;
+ at_start = 1;
+ continue;
+ }
+
+ if (!isdigit(c))
+ goto_bad;
+
+ b = b * 10 + (c - '0');
+ if (!in_range)
+ a = b;
+ at_start = 0;
+ totaldigits++;
+ }
+ if (ndigits == totaldigits)
+ continue;
+ /* if no digit is after '-', it's wrong */
+ if (at_start && in_range)
+ goto_bad;
+ if (!(a <= b))
+ goto_bad;
+ if (b >= nmaskbits)
+ nmaskbits = b + 1;
+ while ((a <= b) && mask) {
+ dm_bit_set(mask, a);
+ a++;
+ }
+ } while (len && c == ',');
+
+ if (!mask) {
+ if (min_num_bits && (nmaskbits < min_num_bits))
+ nmaskbits = min_num_bits;
+
+ if (!(mask = dm_bitset_create(mem, nmaskbits)))
+ goto_bad;
+ str = start;
+ goto scan;
+ }
+
+ return mask;
+bad:
+ if (mask) {
+ if (mem)
+ dm_pool_free(mem, mask);
+ else
+ dm_bitset_destroy(mask);
+ }
+ return NULL;
+}
+
+#if defined(__GNUC__)
+/*
+ * Maintain backward compatibility with older versions that did not
+ * accept a 'min_num_bits' argument to dm_bitset_parse_list().
+ */
+dm_bitset_t dm_bitset_parse_list_v1_02_129(const char *str, struct dm_pool *mem);
+dm_bitset_t dm_bitset_parse_list_v1_02_129(const char *str, struct dm_pool *mem)
+{
+ return dm_bitset_parse_list(str, mem, 0);
+}
+
+#else /* if defined(__GNUC__) */
+
+#endif
diff --git a/device_mapper/datastruct/hash.c b/device_mapper/datastruct/hash.c
new file mode 100644
index 000000000..9b9c939f4
--- /dev/null
+++ b/device_mapper/datastruct/hash.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "misc/dmlib.h"
+
+struct dm_hash_node {
+ struct dm_hash_node *next;
+ void *data;
+ unsigned data_len;
+ unsigned keylen;
+ char key[0];
+};
+
+struct dm_hash_table {
+ unsigned num_nodes;
+ unsigned num_slots;
+ struct dm_hash_node **slots;
+};
+
+/* Permutation of the Integers 0 through 255 */
+static unsigned char _nums[] = {
+ 1, 14, 110, 25, 97, 174, 132, 119, 138, 170, 125, 118, 27, 233, 140, 51,
+ 87, 197, 177, 107, 234, 169, 56, 68, 30, 7, 173, 73, 188, 40, 36, 65,
+ 49, 213, 104, 190, 57, 211, 148, 223, 48, 115, 15, 2, 67, 186, 210, 28,
+ 12, 181, 103, 70, 22, 58, 75, 78, 183, 167, 238, 157, 124, 147, 172,
+ 144,
+ 176, 161, 141, 86, 60, 66, 128, 83, 156, 241, 79, 46, 168, 198, 41, 254,
+ 178, 85, 253, 237, 250, 154, 133, 88, 35, 206, 95, 116, 252, 192, 54,
+ 221,
+ 102, 218, 255, 240, 82, 106, 158, 201, 61, 3, 89, 9, 42, 155, 159, 93,
+ 166, 80, 50, 34, 175, 195, 100, 99, 26, 150, 16, 145, 4, 33, 8, 189,
+ 121, 64, 77, 72, 208, 245, 130, 122, 143, 55, 105, 134, 29, 164, 185,
+ 194,
+ 193, 239, 101, 242, 5, 171, 126, 11, 74, 59, 137, 228, 108, 191, 232,
+ 139,
+ 6, 24, 81, 20, 127, 17, 91, 92, 251, 151, 225, 207, 21, 98, 113, 112,
+ 84, 226, 18, 214, 199, 187, 13, 32, 94, 220, 224, 212, 247, 204, 196,
+ 43,
+ 249, 236, 45, 244, 111, 182, 153, 136, 129, 90, 217, 202, 19, 165, 231,
+ 71,
+ 230, 142, 96, 227, 62, 179, 246, 114, 162, 53, 160, 215, 205, 180, 47,
+ 109,
+ 44, 38, 31, 149, 135, 0, 216, 52, 63, 23, 37, 69, 39, 117, 146, 184,
+ 163, 200, 222, 235, 248, 243, 219, 10, 152, 131, 123, 229, 203, 76, 120,
+ 209
+};
+
+static struct dm_hash_node *_create_node(const char *str, unsigned len)
+{
+ struct dm_hash_node *n = dm_malloc(sizeof(*n) + len);
+
+ if (n) {
+ memcpy(n->key, str, len);
+ n->keylen = len;
+ }
+
+ return n;
+}
+
+static unsigned long _hash(const char *str, unsigned len)
+{
+ unsigned long h = 0, g;
+ unsigned i;
+
+ for (i = 0; i < len; i++) {
+ h <<= 4;
+ h += _nums[(unsigned char) *str++];
+ g = h & ((unsigned long) 0xf << 16u);
+ if (g) {
+ h ^= g >> 16u;
+ h ^= g >> 5u;
+ }
+ }
+
+ return h;
+}
+
+struct dm_hash_table *dm_hash_create(unsigned size_hint)
+{
+ size_t len;
+ unsigned new_size = 16u;
+ struct dm_hash_table *hc = dm_zalloc(sizeof(*hc));
+
+ if (!hc)
+ return_0;
+
+ /* round size hint up to a power of two */
+ while (new_size < size_hint)
+ new_size = new_size << 1;
+
+ hc->num_slots = new_size;
+ len = sizeof(*(hc->slots)) * new_size;
+ if (!(hc->slots = dm_zalloc(len)))
+ goto_bad;
+
+ return hc;
+
+ bad:
+ dm_free(hc->slots);
+ dm_free(hc);
+ return 0;
+}
+
+static void _free_nodes(struct dm_hash_table *t)
+{
+ struct dm_hash_node *c, *n;
+ unsigned i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = n) {
+ n = c->next;
+ dm_free(c);
+ }
+}
+
+void dm_hash_destroy(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ dm_free(t->slots);
+ dm_free(t);
+}
+
+static struct dm_hash_node **_find(struct dm_hash_table *t, const void *key,
+ uint32_t len)
+{
+ unsigned h = _hash(key, len) & (t->num_slots - 1);
+ struct dm_hash_node **c;
+
+ for (c = &t->slots[h]; *c; c = &((*c)->next)) {
+ if ((*c)->keylen != len)
+ continue;
+
+ if (!memcmp(key, (*c)->key, len))
+ break;
+ }
+
+ return c;
+}
+
+void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key,
+ uint32_t len)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ return *c ? (*c)->data : 0;
+}
+
+int dm_hash_insert_binary(struct dm_hash_table *t, const void *key,
+ uint32_t len, void *data)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c)
+ (*c)->data = data;
+ else {
+ struct dm_hash_node *n = _create_node(key, len);
+
+ if (!n)
+ return 0;
+
+ n->data = data;
+ n->next = 0;
+ *c = n;
+ t->num_nodes++;
+ }
+
+ return 1;
+}
+
+void dm_hash_remove_binary(struct dm_hash_table *t, const void *key,
+ uint32_t len)
+{
+ struct dm_hash_node **c = _find(t, key, len);
+
+ if (*c) {
+ struct dm_hash_node *old = *c;
+ *c = (*c)->next;
+ dm_free(old);
+ t->num_nodes--;
+ }
+}
+
+void *dm_hash_lookup(struct dm_hash_table *t, const char *key)
+{
+ return dm_hash_lookup_binary(t, key, strlen(key) + 1);
+}
+
+int dm_hash_insert(struct dm_hash_table *t, const char *key, void *data)
+{
+ return dm_hash_insert_binary(t, key, strlen(key) + 1, data);
+}
+
+void dm_hash_remove(struct dm_hash_table *t, const char *key)
+{
+ dm_hash_remove_binary(t, key, strlen(key) + 1);
+}
+
+static struct dm_hash_node **_find_str_with_val(struct dm_hash_table *t,
+ const void *key, const void *val,
+ uint32_t len, uint32_t val_len)
+{
+ struct dm_hash_node **c;
+ unsigned h;
+
+ h = _hash(key, len) & (t->num_slots - 1);
+
+ for (c = &t->slots[h]; *c; c = &((*c)->next)) {
+ if ((*c)->keylen != len)
+ continue;
+
+ if (!memcmp(key, (*c)->key, len) && (*c)->data) {
+ if (((*c)->data_len == val_len) &&
+ !memcmp(val, (*c)->data, val_len))
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
+int dm_hash_insert_allow_multiple(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len)
+{
+ struct dm_hash_node *n;
+ struct dm_hash_node *first;
+ int len = strlen(key) + 1;
+ unsigned h;
+
+ n = _create_node(key, len);
+ if (!n)
+ return 0;
+
+ n->data = (void *)val;
+ n->data_len = val_len;
+
+ h = _hash(key, len) & (t->num_slots - 1);
+
+ first = t->slots[h];
+
+ if (first)
+ n->next = first;
+ else
+ n->next = 0;
+ t->slots[h] = n;
+
+ t->num_nodes++;
+ return 1;
+}
+
+/*
+ * Look through multiple entries with the same key for one that has a
+ * matching val and return that. If none have maching val, return NULL.
+ */
+void *dm_hash_lookup_with_val(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len)
+{
+ struct dm_hash_node **c;
+
+ c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len);
+
+ return (c && *c) ? (*c)->data : 0;
+}
+
+/*
+ * Look through multiple entries with the same key for one that has a
+ * matching val and remove that.
+ */
+void dm_hash_remove_with_val(struct dm_hash_table *t, const char *key,
+ const void *val, uint32_t val_len)
+{
+ struct dm_hash_node **c;
+
+ c = _find_str_with_val(t, key, val, strlen(key) + 1, val_len);
+
+ if (c && *c) {
+ struct dm_hash_node *old = *c;
+ *c = (*c)->next;
+ dm_free(old);
+ t->num_nodes--;
+ }
+}
+
+/*
+ * Look up the value for a key and count how many
+ * entries have the same key.
+ *
+ * If no entries have key, return NULL and set count to 0.
+ *
+ * If one entry has the key, the function returns the val,
+ * and sets count to 1.
+ *
+ * If N entries have the key, the function returns the val
+ * from the first entry, and sets count to N.
+ */
+void *dm_hash_lookup_with_count(struct dm_hash_table *t, const char *key, int *count)
+{
+ struct dm_hash_node **c;
+ struct dm_hash_node **c1 = NULL;
+ uint32_t len = strlen(key) + 1;
+ unsigned h;
+
+ *count = 0;
+
+ h = _hash(key, len) & (t->num_slots - 1);
+
+ for (c = &t->slots[h]; *c; c = &((*c)->next)) {
+ if ((*c)->keylen != len)
+ continue;
+
+ if (!memcmp(key, (*c)->key, len)) {
+ (*count)++;
+ if (!c1)
+ c1 = c;
+ }
+ }
+
+ if (!c1)
+ return NULL;
+ else
+ return *c1 ? (*c1)->data : 0;
+}
+
+unsigned dm_hash_get_num_entries(struct dm_hash_table *t)
+{
+ return t->num_nodes;
+}
+
+void dm_hash_iter(struct dm_hash_table *t, dm_hash_iterate_fn f)
+{
+ struct dm_hash_node *c, *n;
+ unsigned i;
+
+ for (i = 0; i < t->num_slots; i++)
+ for (c = t->slots[i]; c; c = n) {
+ n = c->next;
+ f(c->data);
+ }
+}
+
+void dm_hash_wipe(struct dm_hash_table *t)
+{
+ _free_nodes(t);
+ memset(t->slots, 0, sizeof(struct dm_hash_node *) * t->num_slots);
+ t->num_nodes = 0u;
+}
+
+char *dm_hash_get_key(struct dm_hash_table *t __attribute__((unused)),
+ struct dm_hash_node *n)
+{
+ return n->key;
+}
+
+void *dm_hash_get_data(struct dm_hash_table *t __attribute__((unused)),
+ struct dm_hash_node *n)
+{
+ return n->data;
+}
+
+static struct dm_hash_node *_next_slot(struct dm_hash_table *t, unsigned s)
+{
+ struct dm_hash_node *c = NULL;
+ unsigned i;
+
+ for (i = s; i < t->num_slots && !c; i++)
+ c = t->slots[i];
+
+ return c;
+}
+
+struct dm_hash_node *dm_hash_get_first(struct dm_hash_table *t)
+{
+ return _next_slot(t, 0);
+}
+
+struct dm_hash_node *dm_hash_get_next(struct dm_hash_table *t, struct dm_hash_node *n)
+{
+ unsigned h = _hash(n->key, n->keylen) & (t->num_slots - 1);
+
+ return n->next ? n->next : _next_slot(t, h + 1);
+}
diff --git a/device_mapper/datastruct/list.c b/device_mapper/datastruct/list.c
new file mode 100644
index 000000000..86c3e4ef8
--- /dev/null
+++ b/device_mapper/datastruct/list.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "misc/dmlib.h"
+#include <assert.h>
+
+/*
+ * Initialise a list before use.
+ * The list head's next and previous pointers point back to itself.
+ */
+void dm_list_init(struct dm_list *head)
+{
+ head->n = head->p = head;
+}
+
+/*
+ * Insert an element before 'head'.
+ * If 'head' is the list head, this adds an element to the end of the list.
+ */
+void dm_list_add(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head;
+ elem->p = head->p;
+
+ head->p->n = elem;
+ head->p = elem;
+}
+
+/*
+ * Insert an element after 'head'.
+ * If 'head' is the list head, this adds an element to the front of the list.
+ */
+void dm_list_add_h(struct dm_list *head, struct dm_list *elem)
+{
+ assert(head->n);
+
+ elem->n = head->n;
+ elem->p = head;
+
+ head->n->p = elem;
+ head->n = elem;
+}
+
+/*
+ * Delete an element from its list.
+ * Note that this doesn't change the element itself - it may still be safe
+ * to follow its pointers.
+ */
+void dm_list_del(struct dm_list *elem)
+{
+ elem->n->p = elem->p;
+ elem->p->n = elem->n;
+}
+
+/*
+ * Remove an element from existing list and insert before 'head'.
+ */
+void dm_list_move(struct dm_list *head, struct dm_list *elem)
+{
+ dm_list_del(elem);
+ dm_list_add(head, elem);
+}
+
+/*
+ * Is the list empty?
+ */
+int dm_list_empty(const struct dm_list *head)
+{
+ return head->n == head;
+}
+
+/*
+ * Is this the first element of the list?
+ */
+int dm_list_start(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->p == head;
+}
+
+/*
+ * Is this the last element of the list?
+ */
+int dm_list_end(const struct dm_list *head, const struct dm_list *elem)
+{
+ return elem->n == head;
+}
+
+/*
+ * Return first element of the list or NULL if empty
+ */
+struct dm_list *dm_list_first(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->n);
+}
+
+/*
+ * Return last element of the list or NULL if empty
+ */
+struct dm_list *dm_list_last(const struct dm_list *head)
+{
+ return (dm_list_empty(head) ? NULL : head->p);
+}
+
+/*
+ * Return the previous element of the list, or NULL if we've reached the start.
+ */
+struct dm_list *dm_list_prev(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_start(head, elem) ? NULL : elem->p);
+}
+
+/*
+ * Return the next element of the list, or NULL if we've reached the end.
+ */
+struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *elem)
+{
+ return (dm_list_end(head, elem) ? NULL : elem->n);
+}
+
+/*
+ * Return the number of elements in a list by walking it.
+ */
+unsigned int dm_list_size(const struct dm_list *head)
+{
+ unsigned int s = 0;
+ const struct dm_list *v;
+
+ dm_list_iterate(v, head)
+ s++;
+
+ return s;
+}
+
+/*
+ * Join two lists together.
+ * This moves all the elements of the list 'head1' to the end of the list
+ * 'head', leaving 'head1' empty.
+ */
+void dm_list_splice(struct dm_list *head, struct dm_list *head1)
+{
+ assert(head->n);
+ assert(head1->n);
+
+ if (dm_list_empty(head1))
+ return;
+
+ head1->p->n = head;
+ head1->n->p = head->p;
+
+ head->p->n = head1->n;
+ head->p = head1->p;
+
+ dm_list_init(head1);
+}