-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ |