/*************************************************************************** * gh_list.c -- a simple doubly-linked list implementation with a very * * heavy focus on efficiency. * * * ***********************IMPORTANT NSOCK LICENSE TERMS*********************** * * * The nsock parallel socket event library is (C) 1999-2012 Insecure.Com * * LLC This library is free software; you may redistribute and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; Version 2. This guarantees * * your right to use, modify, and redistribute this software under certain * * conditions. If this license is unacceptable to you, Insecure.Com LLC * * may be willing to sell alternative licenses (contact * * sales@insecure.com ). * * * * As a special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. You must obey the GNU GPL in all * * respects for all of the code used other than OpenSSL. If you modify * * this file, you may extend this exception to your version of the file, * * but you are not obligated to do so. * * * * If you received these files with a written license agreement stating * * terms other than the (GPL) terms above, then that alternative license * * agreement takes precedence over this comment. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to the dev@nmap.org mailing list for possible incorporation into the * * main distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, or checking them into the Nmap * * source code repository, it is understood (unless you specify otherwise) * * that you are offering the Nmap Project (Insecure.Com LLC) the * * unlimited, non-exclusive right to reuse, modify, and relicense the * * code. Nmap will always be available Open Source, but this is important * * because the inability to relicense code has caused devastating problems * * for other Free Software projects (such as KDE and NASM). We also * * occasionally relicense the code to third parties as discussed above. * * If you wish to specify special license conditions of your * * contributions, just say so when you send them. * * * * This program 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 v2.0 for more details * * (http://www.gnu.org/licenses/gpl-2.0.html). * * * ***************************************************************************/ /* $Id$ */ #include "nsock.h" #include "gh_list.h" #include #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #define SAFETY_CHECK_LIST(l) do { \ assert(l); \ assert((l)->magic == GH_LIST_MAGIC); \ assert((l)->count == 0 || ((l)->first && (l)->last)); \ assert((l)->count != 0 || ((l)->first == NULL && (l)->last == NULL)); \ } while (0) #define SAFETY_CHECK_ELEM(e) do { \ assert(e); \ assert((e)->magic == GH_LIST_MAGIC); \ } while (0) #ifdef GH_LIST_MAIN int main(int argc, char *argv[]) { gh_list lists[16]; gh_list_elem *current, *next; int num = 0; int ret; int i; for(i=0; i < 16; i++) gh_list_init(&lists[i]); for(num=25000; num < 50000; num++) { for(i=0; i < 16; i++) { gh_list_append(&lists[i], (void *)num); } } for(num=24999; num >= 0; num--) { for(i=0; i < 16; i++) { gh_list_prepend(&lists[i], (void *)num); } } for(num=0; num < 50000; num++) { for(i=0; i < 16; i++) { ret = (int) gh_list_pop(&lists[i]); if (ret != num) fatal("prepend_test: Bogus return value %d when expected %d\n", ret, num); } } for(i=0; i < 16; i++) { ret = (int) gh_list_pop(&lists[i]); if (ret != 0) fatal("Ret is bogus for list %d", i); } printf("Done with first set\n"); for(num=24999; num >= 0; num--) { for(i=0; i < 16; i++) { gh_list_prepend(&lists[i], (void *)num); } } for(num=25000; num < 50000; num++) { for(i=0; i < 16; i++) { gh_list_append(&lists[i], (void *)num); } } for(num=0; num < 50000; num++) { for(i=0; i < 16; i++) { ret = (int) gh_list_pop(&lists[i]); if (ret != num) fatal("prepend_test: Bogus return value %d when expected %d\n", ret, num); } } printf("Done with second set\n"); for(num=25000; num < 50000; num++) { for(i=0; i < 16; i++) { gh_list_append(&lists[i], (void *)num); } } for(num=24999; num >= 0; num--) { for(i=0; i < 16; i++) { gh_list_prepend(&lists[i], (void *)num); } } for(num=0; num < 50000; num++) { for(i=0; i < 16; i++) { ret = (int) gh_list_pop(&lists[i]); if (ret != num) fatal("prepend_test: Bogus return value %d when expected %d\n", ret, num); } } printf("Done with third set ...\n"); for(num=24999; num >= 0; num--) { for(i=0; i < 16; i++) { gh_list_prepend(&lists[i], (void *)num); } } for(num=25000; num < 50000; num++) { for(i=0; i < 16; i++) { gh_list_append(&lists[i], (void *)num); } } for(i=0; i < 16; i++) { num=0; for(current = GH_LIST_FIRST_ELEM(&lists[i]); current; current = next) { next = GH_LIST_ELEM_NEXT(current); if ((int)GH_LIST_ELEM_DATA(current) != num) fatal("Got %d when I expected %d\n", (int)GH_LIST_ELEM_DATA(current), num); gh_list_remove_elem(&lists[i], current); num++; } if (num != 50000) fatal("Number is %d, even though %d was expected", num, 50000); if (GH_LIST_COUNT(&lists[i]) != 0) { fatal("List should be empty, but instead it has %d members!\n", GH_LIST_COUNT(&lists[i])); } } printf("Done with fourth set, freeing buffers\n"); for(i=0; i < 16; i++) { gh_list_free(&lists[i]); } } #endif /* GH_LIST_MAIN */ static inline struct gh_list_elem *get_free_buffer(struct gh_list *list) { struct gh_list_elem *newelem; int i; if (!list->free) { list->last_alloc *= 2; list->free = (struct gh_list_elem *)safe_malloc(list->last_alloc * sizeof(struct gh_list_elem)); memset(list->free, 0, list->last_alloc * sizeof(struct gh_list_elem)); list->free->allocated = 1; for (i=0; i < list->last_alloc - 1; i++) { (list->free + i)->next = list->free + i + 1; } } newelem = list->free; list->free = list->free->next; #ifndef NDEBUG newelem->magic = GH_LIST_MAGIC; #endif return newelem; } int gh_list_init(gh_list *newlist) { int i; if (!newlist) return -1; newlist->count = 0; newlist->first = newlist->last = NULL; newlist->last_alloc = 16; newlist->free = (struct gh_list_elem *)safe_malloc(newlist->last_alloc * sizeof(struct gh_list_elem)); memset(newlist->free, 0, newlist->last_alloc * sizeof(struct gh_list_elem)); newlist->free->allocated = 1; for (i = 0; i < newlist->last_alloc - 1; i++) { (newlist->free + i)->next = newlist->free + i + 1; } /* Not needed (newlist->free + newlist->last_alloc - 1)->next = NULL */ #ifndef NDEBUG newlist->magic = GH_LIST_MAGIC; #endif return 0; } gh_list_elem *gh_list_append(gh_list *list, void *data) { gh_list_elem *newelem; gh_list_elem *oldlast; SAFETY_CHECK_LIST(list); newelem = get_free_buffer(list); oldlast = list->last; if (oldlast) { oldlast->next = newelem; newelem->prev = oldlast; } else { newelem->prev = NULL; } newelem->next = NULL; newelem->data = data; #ifndef NDEBUG newelem->magic = GH_LIST_MAGIC; #endif list->count++; list->last = newelem; if (list->count == 1) list->first = newelem; return newelem; } gh_list_elem *gh_list_prepend(gh_list *list, void *data) { gh_list_elem *newelem; gh_list_elem *oldfirst; SAFETY_CHECK_LIST(list); newelem = get_free_buffer(list); oldfirst = list->first; if (oldfirst) { oldfirst->prev = newelem; newelem->next = oldfirst; } else { newelem->next = NULL; } newelem->prev = NULL; newelem->data = data; #ifndef NDEBUG newelem->magic = GH_LIST_MAGIC; #endif list->count++; list->first = newelem; if (list->count == 1) list->last = newelem; return newelem; } gh_list_elem *gh_list_insert_before(gh_list *list, gh_list_elem *before, void *data) { gh_list_elem *newelem; SAFETY_CHECK_LIST(list); SAFETY_CHECK_ELEM(before); /* create or reuse a new cell */ newelem = get_free_buffer(list); newelem->data = data; newelem->prev = before->prev; newelem->next = before; #ifndef NDEBUG newelem->magic = GH_LIST_MAGIC; #endif if (before->prev) before->prev->next = newelem; else list->first = newelem; before->prev = newelem; list->count++; return newelem; } void *gh_list_pop(gh_list *list) { struct gh_list_elem *oldelem; SAFETY_CHECK_LIST(list); oldelem = list->first; if (!oldelem) return NULL; list->first = list->first->next; if (list->first) list->first->prev = NULL; list->count--; if (list->count < 2) list->last = list->first; oldelem->next = list->free; list->free = oldelem; return oldelem->data; } int gh_list_free(gh_list *list) { struct gh_list_elem *current; char *free_list[32]; int free_index = 0; int i = 0; SAFETY_CHECK_LIST(list); #ifndef NDEBUG list->magic++; #endif for (current = list->first; current; current = current->next) { #ifndef NDEBUG current->magic++; #endif if (current->allocated) { assert(free_index < 32); free_list[free_index++] = (char *)current; } } for (current = list->free; current; current = current->next) if (current->allocated) { assert(free_index < 32); free_list[free_index++] = (char *)current; } for (i = 0; i < free_index; i++) free(free_list[i]); return 0; } int gh_list_remove_elem(gh_list *list, gh_list_elem *elem) { SAFETY_CHECK_ELEM(elem); SAFETY_CHECK_LIST(list); if (elem->prev) { elem->prev->next = elem->next; } else { assert(list->first == elem); list->first = elem->next; } if (elem->next) { elem->next->prev = elem->prev; } else { assert(list->last == elem); list->last = elem->prev; } #ifndef NDEBUG elem->magic++; #endif elem->next = list->free; list->free = elem; list->count--; return 0; } int gh_list_move_front(gh_list *list, gh_list_elem *elem) { SAFETY_CHECK_LIST(list); SAFETY_CHECK_ELEM(elem); if (list->first == elem) return 0; /* remove element from its current position */ elem->prev->next = elem->next; if (elem->next) { elem->next->prev = elem->prev; } else { assert(list->last == elem); list->last = elem->prev; } /* add element to the beginning list */ list->first->prev = elem; elem->next = list->first; elem->prev = NULL; list->first = elem; return 0; } int gh_list_remove(gh_list *list, void *data) { struct gh_list_elem *current; SAFETY_CHECK_LIST(list); for (current = list->first; current; current = current->next) { if (current->data == data) return gh_list_remove_elem(list, current); } return -1; }