/* linkedlist - a singularly linked list
* Copyright (c) 2002 Michael B. Allen <mba2000 ioplex.com>
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include "mba/msgno.h"
#include "mba/iterator.h"
#include "mba/allocator.h"
#include "mba/linkedlist.h"
void
linkedlist_print(struct linkedlist *l)
{
struct node *n = l->first;
int idx = 0;
while (n) {
fprintf(stderr, "list node %d n=%p,data=%p\n", idx, n, n->data);
n = n->ptr;
idx++;
}
}
static void
_cache_remove_by_node(struct linkedlist *l, struct node *n)
{
struct cache_entry *ce;
int i;
for (i = 0; i < CACHE_SIZE; i++) {
ce = l->cache + i;
if (ce->ent == n) {
ce->ent = NULL;
}
}
}
static void
_cache_update_by_index(struct linkedlist *l, unsigned int idx, int insert)
{
struct cache_entry *ce;
int i;
for (i = 0; i < CACHE_SIZE; i++) {
ce = l->cache + i;
if (ce->ent && ce->idx >= idx) {
ce->idx += insert ? 1 : -1;
}
}
}
int
linkedlist_init(struct linkedlist *l, unsigned int max_size, struct allocator *al)
{
if (l == NULL) {
errno = EINVAL;
PMNO(errno);
return -1;
}
memset(l, 0, sizeof *l);
l->max_size = max_size == 0 ? INT_MAX : max_size;
l->al = al;
return 0;
}
int
linkedlist_deinit(struct linkedlist *l, del_fn data_del, void *context)
{
int ret = 0;
if (l) {
struct node *next = l->first;
while (next != NULL) {
struct node *tmp;
if (data_del) {
ret += data_del(context, next->data);
}
tmp = next;
next = next->ptr;
ret += allocator_free(l->al, tmp);
}
}
return ret ? -1 : 0;
}
struct linkedlist *
linkedlist_new(unsigned int max_size, struct allocator *al)
{
struct linkedlist *l;
if ((l = allocator_alloc(al, sizeof *l, 0)) == NULL) {
PMNO(errno);
return NULL;
}
linkedlist_init(l, max_size, al);
return l;
}
int
linkedlist_del(struct linkedlist *l, del_fn data_del, void *context)
{
int ret = 0;
if (l) {
ret = linkedlist_deinit(l, data_del, context) + allocator_free(l->al, l);
}
return ret ? -1 : 0;
}
int
linkedlist_clear(struct linkedlist *l, del_fn data_del, void *context)
{
if (l) {
int max_size = l->max_size;
struct allocator *al = l->al;
if (linkedlist_deinit(l, data_del, context) == -1) {
AMSG("");
return -1;
}
linkedlist_init(l, max_size, al);
}
return 0;
}
int
linkedlist_add(struct linkedlist *l, const void *data)
{
struct node *n;
if (l == NULL) {
PMNF(errno = EINVAL, ": l=NULL");
return -1;
}
if (l->size == l->max_size) {
PMNF(errno = ERANGE, ": size=%u,max_size=%u", l->size, l->max_size);
return -1;
}
if ((n = allocator_alloc(l->al, sizeof *n, 0)) == NULL) {
PMNO(errno);
return -1;
}
n->data = (void *)data;
n->ptr = NULL;
if (l->size == 0) {
l->first = l->last = n;
} else {
l->last->ptr = n;
l->last = n;
}
l->size++;
return 0;
}
int
linkedlist_insert(struct linkedlist *l, unsigned int idx, const void *data)
{
struct node *n;
if (l == NULL || data == NULL) {
PMNF(errno = ERANGE, ": l=%p,data=%p", l, data);
return -1;
}
if (idx > l->size || l->size == l->max_size) {
PMNF(errno = ERANGE, ": idx=%u,size=%u,max_size=%u", idx, l->size, l->max_size);
return -1;
}
if ((n = allocator_alloc(l->al, sizeof *n, 0)) == NULL) {
PMNO(errno);
return -1;
}
n->data = (void *)data;
n->ptr = NULL;
if (l->size == 0) {
l->first = l->last = n;
} else {
if (idx == 0) {
n->ptr = l->first;
l->first = n;
} else if (idx == l->size) {
l->last->ptr = n;
l->last = n;
} else {
struct node *tmp;
unsigned int i;
tmp = l->first;
n->ptr = tmp->ptr;
for (i = 1; i < idx; i++) {
tmp = tmp->ptr;
n->ptr = tmp->ptr;
}
tmp->ptr = n;
}
}
l->size++;
_cache_update_by_index(l, idx, 1); /* increment cache entries with larger idx */
return 0;
}
int
linkedlist_insert_sorted(struct linkedlist *l,
cmp_fn cmp,
void *context,
void **replaced,
const void *data)
{
struct node *n, *p;
int c;
unsigned int idx;
int ins = 1;
if (l == NULL || cmp == NULL || data == NULL) {
PMNF(errno = EINVAL, ": l=%p,cmp=%p,data=%p", l, cmp, data);
return -1;
}
if (l->size == l->max_size) {
PMNF(errno = ERANGE, ": size=%u,max_size=%u", l->size, l->max_size);
return -1;
}
if ((n = allocator_alloc(l->al, sizeof *n, 0)) == NULL) {
PMNO(errno);
return -1;
}
n->data = (void *)data;
idx = 0;
p = NULL;
n->ptr = l->first;
while (n->ptr) {
c = cmp(data, n->ptr->data, context);
if (c < 0 || (replaced && c == 0)) {
if (c == 0) { /* replace */
struct node *rm = n->ptr;
*replaced = rm->data;
n->ptr = rm->ptr;
_cache_remove_by_node(l, rm);
allocator_free(l->al, rm);
l->size--;
ins = 0;
}
break;
}
p = n->ptr;
n->ptr = n->ptr->ptr;
idx++;
}
if (p) {
p->ptr = n;
} else {
l->first = n;
}
if (n->ptr == NULL) {
l->last = n;
}
l->size++;
if (ins) {
_cache_update_by_index(l, idx, 1); /* increment cache entries with larger idx */
}
return idx;
}
int
linkedlist_is_empty(const struct linkedlist *l)
{
return l == NULL || l->size == 0;
}
unsigned int
linkedlist_size(const struct linkedlist *l)
{
return l == NULL ? 0 : l->size;
}
void
linkedlist_iterate(void *l, iter_t *iter)
{
if (l && iter) {
iter->i1 = 0;
}
}
void *
linkedlist_next(void *l, iter_t *iter)
{
if (l && iter) {
struct linkedlist *l0 = l;
if (iter->i1 >= l0->size) {
return NULL;
}
/* optimized using cache to be O(1) if elements accessed sequentially */
return linkedlist_get(l0, iter->i1++);
}
return NULL;
}
void *
linkedlist_get(struct linkedlist *l, unsigned int idx)
{
if (l == NULL) {
errno = EINVAL;
PMNF(errno, ": l=%p", l);
return NULL;
}
if (idx >= l->size) {
PMNF(errno = ERANGE, ": idx=%u,size=%u", idx, l->size);
return NULL;
}
if (idx == 0) {
return l->first->data;
} else if (idx == l->size - 1) {
return l->last->data;
} else {
unsigned int i, closest_idx = (unsigned int)-1;
struct cache_entry *ce, *stale = NULL, *closest = NULL;
for (i = 0; i < CACHE_SIZE && closest_idx; i++) {
ce = l->cache + i;
if (ce->ent == NULL) {
stale = ce;
continue;
}
if (idx >= ce->idx && (idx - ce->idx) < closest_idx) {
closest_idx = ce->idx;
closest = ce;
} else if (stale == NULL) {
stale = ce;
}
}
if (closest_idx == (unsigned int)-1) {
struct node *next = l->first;
ce = stale;
for (i = 0; i < idx; i++) {
next = next->ptr;
}
ce->idx = i;
ce->ent = next;
} else {
ce = closest;
while (ce->idx < idx) {
ce->idx++;
ce->ent = ce->ent->ptr;
if (ce->ent == NULL) {
return NULL;
}
}
}
return ce->ent->data;
}
}
void *
linkedlist_get_last(const struct linkedlist *l)
{
if (l == NULL) {
PMNF(errno = EINVAL, ": l=%p", l);
return NULL;
}
if (l->size == 0) {
return NULL;
}
return l->last->data;
}
void *
linkedlist_remove(struct linkedlist *l, unsigned int idx)
{
void *result;
struct node *tmp;
if (l == NULL) {
PMNF(errno = EINVAL, ": l=%p", l);
return NULL;
}
if (idx >= l->size) {
return NULL;
}
if (idx == 0) {
result = l->first->data;
tmp = l->first;
l->first = l->first->ptr;
} else {
struct node *n;
unsigned int i;
n = l->first;
for (i = 1; i < idx; i++) {
n = n->ptr;
}
tmp = n->ptr;
n->ptr = tmp->ptr;
if (tmp == l->last) {
l->last = n;
}
result = tmp->data;
}
_cache_remove_by_node(l, tmp);
allocator_free(l->al, tmp);
l->size--;
_cache_update_by_index(l, idx, 0); /* decrement cache entries with larger idx */
return result;
}
void *
linkedlist_remove_last(struct linkedlist *l)
{
void *result;
if (l == NULL) {
PMNF(errno = EINVAL, ": l=%p", l);
return NULL;
}
if (l->size == 0) {
return NULL;
}
if (l->size == 1) {
result = l->first->data;
_cache_remove_by_node(l, l->first);
allocator_free(l->al, l->first);
l->first = l->last = NULL;
} else {
struct node *n;
result = l->last->data;
n = l->first;
while (n->ptr != l->last) {
n = n->ptr;
}
_cache_remove_by_node(l, l->last);
allocator_free(l->al, l->last);
l->last = n;
n->ptr = NULL;
}
l->size--;
return result;
}
void *
linkedlist_remove_data(struct linkedlist *l, const void *data)
{
struct node *tmp;
if (l == NULL || data == NULL) {
PMNF(errno = EINVAL, ": l=%p", l);
return NULL;
}
if (l->size == 0) {
return NULL;
}
if (data == l->first->data) {
tmp = l->first;
l->first = l->first->ptr;
} else {
struct node *n;
int idx = 1;
for (n = l->first; n->ptr && n->ptr->data != data; n = n->ptr) {
idx++;
}
if (n->ptr == NULL) {
return NULL;
}
tmp = n->ptr;
n->ptr = tmp->ptr;
if (tmp == l->last) {
l->last = n;
}
_cache_update_by_index(l, idx, 0); /* decrement cache entries with larger idx */
}
_cache_remove_by_node(l, tmp);
allocator_free(l->al, tmp);
l->size--;
return (void *)data;
}
int
linkedlist_toarray(struct linkedlist *l, void *array[])
{
struct node *n;
int i;
if (l == NULL || array == NULL) {
PMNF(errno = EINVAL, ": l=%p", l);
return -1;
}
n = l->first;
for (i = 0; n; i++) {
array[i] = n->data;
n = n->ptr;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1