Skip to content

Commit

Permalink
add linked list implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
uno20001 committed Apr 20, 2020
1 parent a0c182f commit 7af5ab4
Show file tree
Hide file tree
Showing 2 changed files with 283 additions and 0 deletions.
214 changes: 214 additions & 0 deletions ll.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
This file is part of mktorrent
mktorrent is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
mktorrent is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU 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 <stdlib.h>
#include <string.h>

#include "export.h"
#include "ll.h"


EXPORT struct ll *ll_new(void)
{
struct ll *list = calloc(1, sizeof(*list));

if (list)
LL_TAIL(list) = &list->head;

return list;
}

EXPORT void ll_free(struct ll *list, ll_node_data_destructor destructor)
{
if (list) {

struct ll_node *head = LL_HEAD(list), *next;

while (head) {

if (destructor)
destructor(LL_DATA(head));

if (head->data_size)
free(LL_DATA(head));

next = LL_NEXT(head);

free(head);

head = next;
}

free(list);
}
}

static struct ll_node *ll_node_new(
void *data,
const size_t data_size,
struct ll_node *prev,
struct ll_node *next)
{
struct ll_node *node = calloc(1, sizeof(*node));

if (!node)
return NULL;

if (data_size) {
LL_DATA(node) = calloc(1, data_size);

if (!LL_DATA(node))
goto oom_node_data;

memcpy(LL_DATA(node), data, data_size);
}
else
LL_DATA(node) = data;

LL_DATASIZE(node) = data_size;
LL_PREV(node) = prev;
LL_NEXT(node) = next;

return node;

oom_node_data:
free(node);

return NULL;
}

/* appends a new node with 'data' to the end of 'list' */
EXPORT struct ll_node *ll_append(struct ll *list, void *data, const size_t data_size)
{
if (!list)
return NULL;

struct ll_node *node = ll_node_new(data, data_size, LL_TAIL(list), NULL);

if (node) {
LL_NEXT(LL_TAIL(list)) = node;
LL_TAIL(list) = node;
}

return node;
}

/* concatenates two lists while destroying the second one */
EXPORT struct ll *ll_extend(struct ll *list, struct ll *other)
{
if (!list)
return NULL;

if (!other)
return list;

LL_NEXT(LL_TAIL(list)) = LL_HEAD(other);

free(other);

return list;
}

/* sort the given range using recursive merge sort in a stable way;
* sets 'first' to the new head, 'last' to the new tail;
* the new head will have ->prev = NULL,
* and the new tail will have ->next = NULL;
*/
static void ll_sort_node_range(
struct ll_node **first,
struct ll_node **last,
ll_node_data_cmp cmp)
{
#define APPEND_AND_STEP(t, x) do { \
LL_NEXT(t) = (x); \
LL_PREV(x) = (t); \
\
LL_STEP(x); \
LL_STEP(t); \
} while(0)

if (first == NULL || *first == NULL || last == NULL || *last == NULL)
return;

/* sorting a one element range is trivial */
if (*first == *last) {
LL_CLEAR_LINKS(*first);
return;
}

struct ll_node *middle = *first, *middle2 = *last;

while (middle != middle2 && LL_NEXT(middle) != middle2) {
middle = LL_NEXT(middle);
middle2 = LL_PREV(middle2);
}

/* middle is now the midpoint of the list */

/* 'tail' is the tail of the new, sorted list */
struct ll_node dummy, *tail = &dummy;

struct ll_node *a = *first;
struct ll_node *b = LL_NEXT(middle);

/* the values of middle and *last are not used anymore in this function,
* so they can be safely overwritten by the recursive calls
*/
ll_sort_node_range(&a, &middle, cmp);
ll_sort_node_range(&b, last, cmp);

while (a && b) {
int r = cmp(LL_DATA(a), LL_DATA(b));

if (r <= 0) APPEND_AND_STEP(tail, a); /* if a.val <= b.val, append a */
else APPEND_AND_STEP(tail, b); /* otherwise, append b */
}

/* at this point only one of a or b might be non-NULL,
* so only one of the next two loops will run
*/

/* append remaining nodes from the first half */
while (a) APPEND_AND_STEP(tail, a);

/* append remaining nodes from the second half */
while (b) APPEND_AND_STEP(tail, b);

/* the prev ptr of the first "real" node points to dummy, clear that */
LL_PREV(LL_NEXT(&dummy)) = NULL;

/* set the new head and tail */
*first = LL_NEXT(&dummy);
*last = tail;

#undef APPEND_AND_STEP
}

EXPORT struct ll *ll_sort(struct ll *list, ll_node_data_cmp cmp)
{
if (list == NULL || cmp == NULL)
return NULL;

ll_sort_node_range(&LL_HEAD(list), &LL_TAIL(list), cmp);

if (LL_HEAD(list))
LL_PREV(LL_HEAD(list)) = &list->head;

return list;
}
69 changes: 69 additions & 0 deletions ll.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#ifndef MKTORRENT_LL_H
#define MKTORRENT_LL_H

struct ll_node {
struct ll_node *prev, *next;

size_t data_size;
void *data;
};

struct ll {
struct ll_node head, *tail;
};

typedef void (*ll_node_data_destructor)(void *);
typedef int (*ll_node_data_cmp)(const void *, const void *);


#define LL_DATA(node) ((node)->data)
#define LL_DATA_AS(node, type) ((type) LL_DATA(node))
#define LL_DATASIZE(node) ((node)->data_size)
#define LL_PREV(node) ((node)->prev)
#define LL_NEXT(node) ((node)->next)
#define LL_STEP(node) ((node) = LL_NEXT(node))
#define LL_STEP_PREV(node) ((node) = LL_PREV(node))
#define LL_CLEAR_LINKS(node) do { LL_NEXT(node) = NULL; LL_PREV(node) = NULL; } while(0)

#define LL_HEAD(list) ((list)->head.next)
#define LL_TAIL(list) ((list)->tail)
#define LL_IS_EMPTY(list) (LL_HEAD(list) == NULL)
#define LL_IS_SINGLETON(list) (!LL_IS_EMPTY(list) && LL_NEXT(LL_HEAD(list)) == NULL)
#define LL_FOR_FROM_TO_STEP(node, from, to, step) for (struct ll_node *(node) = from; node != to; step(node))
#define LL_FOR(node, list) LL_FOR_FROM_TO_STEP(node, LL_HEAD(list), NULL, LL_STEP)
#define LL_FOR_FROM(node, from) LL_FOR_FROM_TO_STEP(node, from, NULL, LL_STEP)


/* creates a new linked list instance */
EXPORT struct ll *ll_new(void);


/* frees the given list, calls a "destructor" function
* on the data pointers if provided
*/
EXPORT void ll_free(struct ll *, ll_node_data_destructor);


/* appends a new node with data to the end of the given list,
* if the provided size is zero, then the data pointer is set to
* the pointer provided in the arguments, otherwise size number of
* "bytes" is allocated, and that amount of bytes is copied into
* this newly allocated node from the given pointer
*/
EXPORT struct ll_node *ll_append(struct ll *, void *, const size_t);


/* concatenates the second list to the first one
* while destroying the second one,
* returns the first one
*/
EXPORT struct ll *ll_extend(struct ll *, struct ll *);


/* sorts the given list using recursive merge sort based on
* the provieded comparator function, fails if either of the arguments is
* NULL, returns the given list
*/
EXPORT struct ll *ll_sort(struct ll *, ll_node_data_cmp);

#endif /* MKTORRENT_LL_H */

0 comments on commit 7af5ab4

Please sign in to comment.