/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    This program 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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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 for more details.
  
    You should have received a copy of the GNU General Public License
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/


/* midnightcode-list.h :: Midnight Code list library			*/

#include <unistd.h>

#ifndef __MIDNIGHTCODE_LIST_H
#define __MIDNIGHTCODE_LIST_H


#ifdef MIDNIGHTCODE_LIST_NOLOCK
# define midnightcode_list_lock(head)
#else
# define midnightcode_list_lock(head)				\
  (head)->lock = 1;
#endif

#ifdef MIDNIGHTCODE_LIST_NOLOCK
# define midnightcode_list_unlock(head)
#else
# define midnightcode_list_unlock(head)				\
  (head)->lock = 0;
#endif

#ifdef MIDNIGHTCODE_LIST_NOLOCK
# define midnightcode_list_spin_lock(head)
#else
# define midnightcode_list_spin_lock(head)			\
  while((head)->lock)						\
    usleep(250);
#endif

#define midnightcode_list_head(name, list_struct)		\
  struct name {							\
    struct list_struct *first;					\
    struct list_struct *last;					\
    struct list_struct *hold;					\
    int count;							\
    int lock;							\
  } name;

#define midnightcode_list_init(name, list_struct)		\
  struct name {							\
    struct list_struct *first;					\
    struct list_struct *last;					\
    struct list_struct *hold;					\
    int count;							\
    int lock;							\
  } name;							\
  (&(name))->first = NULL;					\
  (&(name))->last  = NULL;					\
  (&(name))->hold  = NULL;					\
  (&(name))->count = 0;						\
  (&(name))->lock  = 0;

#define midnightcode_list_for_each(entry, head)			\
  for(entry = (head)->first; entry != NULL;			\
    entry = entry->MIDNIGHTCODE_LIST_ENTRIES.next)

#ifdef MIDNIGHTCODE_LIST_NOINDEX
# define midnightcode_list_renumber(head, entry)
#else
# define midnightcode_list_renumber(head, entry)		\
  (head)->hold = (entry);					\
  if(entry == NULL ||						\
    entry->MIDNIGHTCODE_LIST_ENTRIES.prev == NULL) {		\
    entry = (head)->first;					\
  } else {							\
    entry = entry->MIDNIGHTCODE_LIST_ENTRIES.prev;		\
  }								\
  for(; entry != NULL;						\
    entry = entry->MIDNIGHTCODE_LIST_ENTRIES.next) {		\
    if(entry->MIDNIGHTCODE_LIST_ENTRIES.prev == NULL) {		\
      entry->MIDNIGHTCODE_LIST_ENTRIES.index = 0;		\
    } else {							\
      entry->MIDNIGHTCODE_LIST_ENTRIES.index =			\
        entry->MIDNIGHTCODE_LIST_ENTRIES.prev->			\
        MIDNIGHTCODE_LIST_ENTRIES.index + 1;			\
    }								\
  }								\
  entry = (head)->hold;						\
  (head)->hold = NULL;
#endif

#define midnightcode_list_entry(list_struct)			\
  struct {							\
    int index;							\
    struct list_struct *next;					\
    struct list_struct *prev;					\
  } MIDNIGHTCODE_LIST_ENTRIES;

#define midnightcode_list_insert_before(head, before, entry)	\
  if((before) != (entry) &&					\
    (before) != NULL &&						\
    (entry) != NULL) {						\
    midnightcode_list_spin_lock(head);				\
    midnightcode_list_lock(head);				\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next = (before);		\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev =			\
    (before)->MIDNIGHTCODE_LIST_ENTRIES.prev;			\
    if((entry)->MIDNIGHTCODE_LIST_ENTRIES.prev != NULL)		\
      (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev->			\
      MIDNIGHTCODE_LIST_ENTRIES.next = (entry);			\
    (before)->MIDNIGHTCODE_LIST_ENTRIES.prev = (entry);		\
    if((head)->first == (before))				\
      (head)->first = (entry);					\
    (head)->count++;						\
    midnightcode_list_renumber(head, entry);			\
    midnightcode_list_unlock(head);				\
  }

#define midnightcode_list_insert_after(head, after, entry)	\
  if((after) != (entry) &&					\
    (after) != NULL &&						\
    (entry) != NULL) {						\
    midnightcode_list_spin_lock(head);				\
    midnightcode_list_lock(head);				\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next =			\
    (after)->MIDNIGHTCODE_LIST_ENTRIES.next;			\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev = (after);		\
    if((entry)->MIDNIGHTCODE_LIST_ENTRIES.next != NULL)		\
      (entry)->MIDNIGHTCODE_LIST_ENTRIES.next->			\
      MIDNIGHTCODE_LIST_ENTRIES.prev = (entry);			\
    (after)->MIDNIGHTCODE_LIST_ENTRIES.next = (entry);		\
    if((head)->last == (after))					\
      (head)->last = (entry);					\
    (head)->count++;						\
    midnightcode_list_renumber(head, entry);			\
    midnightcode_list_unlock(head);				\
  }

#define midnightcode_list_insert_tail(head, entry)		\
  midnightcode_list_spin_lock(head);				\
  midnightcode_list_lock(head);					\
  if((head)->last != NULL) {					\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev = (head)->last;	\
    (head)->last->MIDNIGHTCODE_LIST_ENTRIES.next = (entry);	\
  } else {							\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev = NULL;		\
    (head)->first = (entry);					\
  }								\
  (head)->last = (entry);					\
  (entry)->MIDNIGHTCODE_LIST_ENTRIES.next = NULL;		\
  (head)->count++;						\
  midnightcode_list_renumber(head, entry);			\
  midnightcode_list_unlock(head);

#define midnightcode_list_append(head, entry)			\
  midnightcode_list_insert_tail(head, entry);

#define midnightcode_list_insert_head(head, entry)		\
  midnightcode_list_spin_lock(head);				\
  midnightcode_list_lock(head);					\
  if((head)->first != NULL) {					\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next = (head)->first;	\
    (head)->first->MIDNIGHTCODE_LIST_ENTRIES.prev = (entry);	\
  } else {							\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next = NULL;		\
    (head)->last = (entry);					\
  }								\
  (head)->first = (entry);					\
  (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev = NULL;		\
  (head)->count++;						\
  midnightcode_list_renumber(head, entry);			\
  midnightcode_list_unlock(head);

#define midnightcode_list_insert(head, entry)			\
  midnightcode_list_insert_head(head, entry);

#define midnightcode_list_remove(head, entry)			\
  midnightcode_list_spin_lock(head);				\
  midnightcode_list_lock(head);					\
  if((head)->first == (entry)) {				\
    if((entry)->MIDNIGHTCODE_LIST_ENTRIES.next != NULL) {	\
      (head)->first = (entry)->MIDNIGHTCODE_LIST_ENTRIES.next;	\
    } else {							\
      (head)->first = NULL;					\
      (head)->last = NULL;					\
    }								\
  }								\
  if((head)->last == (entry)) {					\
    if((entry)->MIDNIGHTCODE_LIST_ENTRIES.prev != NULL) {	\
      (head)->last = (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev;	\
    } else {							\
      (head)->first = NULL;					\
      (head)->last = NULL;					\
    }								\
  }								\
  if((entry)->MIDNIGHTCODE_LIST_ENTRIES.next != NULL)		\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next->			\
    MIDNIGHTCODE_LIST_ENTRIES.prev =				\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev;			\
  if((entry)->MIDNIGHTCODE_LIST_ENTRIES.prev != NULL)		\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.prev->			\
    MIDNIGHTCODE_LIST_ENTRIES.next =				\
    (entry)->MIDNIGHTCODE_LIST_ENTRIES.next;			\
  (head)->count--;						\
  midnightcode_list_renumber(head, entry);			\
  midnightcode_list_unlock(head);

#define midnightcode_list_count(head)				\
  (head)->count

#define midnightcode_list_entry_index(entry)			\
  (entry)->MIDNIGHTCODE_LIST_ENTRIES.index


#endif
