/* Copyright 2005 Chris Thomasson */


#ifndef AC_DLIST_H
#define AC_DLIST_H


#ifdef __cplusplus
extern "C"
{
#endif




#ifdef _MSC_VER 
#pragma pack(1) 
#endif


/* MUST be two adjacent words! */
typedef struct
AC_DECLSPEC_PACKED
ac_dlist_node_
{
  struct ac_dlist_node_ *next;
  struct ac_dlist_node_ *prev;

} ac_dlist_node_t;


/* MUST be two adjacent words! */
typedef struct
AC_DECLSPEC_PACKED
ac_dlist_
{
  ac_dlist_node_t *front;
  ac_dlist_node_t *back;

} ac_dlist_t;


#ifdef _MSC_VER 
#pragma pack() 
#endif




/* critical dlist compile time assertion */
AC_BUILD_DBG_ASSERT
( dlist,
  sizeof( ac_dlist_node_t ) == sizeof( ac_cpu_node_t ) / 2 &&
  sizeof( ac_dlist_node_t ) == AC_CPU_WORD_SIZE * 2 &&
  sizeof( ac_dlist_t ) == AC_CPU_WORD_SIZE * 2 );




AC_DECLSPEC_INLINE void AC_APIDECL
ac_dlist_init
( ac_dlist_t *_this )
{
  _this->front = 0;
  _this->back = 0;
}




#define ac_dlist_get_front( ac_macro_this ) \
  ( (ac_macro_this)->front )


#define ac_dlist_get_back( ac_macro_this ) \
  ( (ac_macro_this)->back )


#define ac_mb_dlist_get_front( ac_macro_this ) \
  ( (ac_dlist_node_t*)ac_mb_loadptr_depends( &(ac_macro_this)->front ) )


#define ac_mb_dlist_get_back( ac_macro_this ) \
  ( (ac_dlist_node_t*)ac_mb_loadptr_depends( &(ac_macro_this)->back ) )




#define ac_dlist_node_get_next( ac_macro_this ) \
  ( (ac_macro_this)->next )


#define ac_dlist_node_get_prev( ac_macro_this ) \
  ( (ac_macro_this)->prev )


#define ac_mb_dlist_node_get_next( ac_macro_this ) \
  ( (ac_dlist_node_t*)ac_mb_loadptr_depends( &(ac_macro_this)->next ) )


#define ac_mb_dlist_node_get_prev( ac_macro_this ) \
  ( (ac_dlist_node_t*)ac_mb_loadptr_depends( &(ac_macro_this)->prev ) )




AC_DECLSPEC_INLINE void AC_APIDECL
ac_dlist_pop
( ac_dlist_t *_this,
  ac_dlist_node_t *node )
{
  ac_dlist_node_t 
    *next = node->next,
    *prev = node->prev;

  assert( _this->front &&
          _this->back );

  if ( ! next && ! prev )
  {
    _this->front = 0;
    _this->back = 0;
  }

  else if ( next && ! prev )
  {
    next->prev = 0;
    _this->front = next;
  }

  else if ( ! next && prev )
  {
    prev->next = 0;
    _this->back = prev;
  }

  else
  {
    next->prev = prev;
    prev->next = next;
  }
}


AC_DECLSPEC_INLINE void AC_APIDECL
ac_dlist_push_back
( ac_dlist_t *_this,
  ac_dlist_node_t *node )
{
  node->next = 0;
  node->prev = 0;

  if ( ! _this->front )
  {
    assert( ! _this->front && 
            ! _this->back );

    _this->front = node;
    _this->back = node;
  }

  else
  {
    node->prev = _this->back;
    _this->back->next = node;
    _this->back = node;
  }
}


AC_DECLSPEC_INLINE ac_dlist_node_t* AC_APIDECL
ac_dlist_pop_front
( ac_dlist_t *_this )
{
  ac_dlist_node_t *front = _this->front;

  if ( front )
  {
    ac_dlist_pop( _this, front );
  }

  return front;
}


AC_DECLSPEC_INLINE ac_dlist_node_t* AC_APIDECL
ac_dlist_pop_back
( ac_dlist_t *_this )
{
  ac_dlist_node_t *back = _this->back;

  if ( back )
  {
    ac_dlist_pop( _this, back );
  }

  return back;
}


AC_DECLSPEC_INLINE void AC_APIDECL
ac_mb_dlist_pop
( ac_dlist_t *_this,
  ac_dlist_node_t *node )
{
  ac_dlist_node_t *next, *prev;

  next = (ac_dlist_node_t*)ac_mb_loadptr_depends( &node->next );
  prev = (ac_dlist_node_t*)ac_mb_loadptr_depends( &node->prev );

  assert( _this->front &&
          _this->back );

  if ( ! next && ! prev )
  {   
    ac_mb_storeptr_release( &_this->front, 0 );
    ac_mb_storeptr_release( &_this->back, 0 );
  }

  else if ( next && ! prev )
  {
    ac_mb_storeptr_release( &next->prev, 0 );
    ac_mb_storeptr_release( &_this->front, next );
  }

  else if ( ! next && prev )
  {
    ac_mb_storeptr_release( &prev->next, 0 );
    ac_mb_storeptr_release( &_this->back, prev );
  }

  else
  {
    ac_mb_storeptr_release( &next->prev, prev );
    ac_mb_storeptr_release( &prev->next, next );
  }
}


AC_DECLSPEC_INLINE void AC_APIDECL
ac_mb_dlist_push_back
( ac_dlist_t *_this,
  ac_dlist_node_t *node )
{
  node->next = 0;
  node->prev = 0;

  if ( ! _this->front )
  {
    assert( ! _this->front && 
            ! _this->back );
    
    ac_mb_storeptr_release( &_this->front, node );
    ac_mb_storeptr_release( &_this->back, node );
  }

  else
  {
    ac_dlist_node_t *back = 
      (ac_dlist_node_t*)
        ac_mb_loadptr_depends( &_this->back );

    ac_mb_storeptr_release( &node->prev, back );
    ac_mb_storeptr_release( &back->next, node );
    ac_mb_storeptr_release( &_this->back, node );    
  }
}


AC_DECLSPEC_INLINE ac_dlist_node_t* AC_APIDECL
ac_mb_dlist_pop_front
( ac_dlist_t *_this )
{
  ac_dlist_node_t *front = 
    (ac_dlist_node_t*)
      ac_mb_loadptr_depends( &_this->front );

  if ( front )
  {
    ac_mb_dlist_pop( _this, front );
  }

  return front;
}


AC_DECLSPEC_INLINE ac_dlist_node_t* AC_APIDECL
ac_mb_dlist_pop_back
( ac_dlist_t *_this )
{
  ac_dlist_node_t *back = 
    (ac_dlist_node_t*)
      ac_mb_loadptr_depends( &_this->back );

  if ( back )
  {
    ac_mb_dlist_pop( _this, back );
  }

  return back;
}




#define ac_dlist_pop_cast( ac_macro_this, ac_macro_node ) \
  ( ac_dlist_pop \
      ( (ac_dlist_t*)(ac_macro_this), \
        (ac_dlist_node_t*)(ac_macro_node) ) )


#define ac_dlist_pop_front_cast( ac_macro_this ) \
  ( (void*)ac_dlist_pop_front \
      ( (ac_dlist_t*)(ac_macro_this) ) )


#define ac_dlist_pop_back_cast( ac_macro_this ) \
  ( (void*)ac_dlist_pop_back \
      ( (ac_dlist_t*)(ac_macro_this) ) )


#define ac_dlist_push_back_cast( ac_macro_this, ac_macro_node ) \
  ( ac_dlist_push_back \
      ( (ac_dlist_t*)(ac_macro_this), \
        (ac_dlist_node_t*)(ac_macro_node) ) )


#define ac_mb_dlist_pop_cast( ac_macro_this, ac_macro_node ) \
  ( ac_mb_dlist_pop \
      ( (ac_dlist_t*)(ac_macro_this), \
        (ac_dlist_node_t*)(ac_macro_node) ) )


#define ac_mb_dlist_pop_front_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_pop_front \
      ( (ac_dlist_t*)(ac_macro_this) ) )


#define ac_mb_dlist_pop_back_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_pop_back \
      ( (ac_dlist_t*)(ac_macro_this) ) )


#define ac_mb_dlist_push_back_cast( ac_macro_this, ac_macro_node ) \
  ( ac_mb_dlist_push_back \
      ( (ac_dlist_t*)(ac_macro_this), \
        (ac_dlist_node_t*)(ac_macro_node) ) )




#define ac_dlist_get_front_cast( ac_macro_this ) \
  ( (void*)ac_dlist_get_front( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_dlist_get_back_cast( ac_macro_this ) \
  ( (void*)ac_dlist_get_back( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_mb_dlist_get_front_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_get_front( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_mb_dlist_get_back_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_get_back( (ac_dlist_node_t*)(ac_macro_this) ) )




#define ac_dlist_node_get_next_cast( ac_macro_this ) \
  ( (void*)ac_dlist_node_get_next( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_dlist_node_get_prev_cast( ac_macro_this ) \
  ( (void*)ac_dlist_node_get_prev( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_mb_dlist_node_get_next_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_node_get_next( (ac_dlist_node_t*)(ac_macro_this) ) )


#define ac_mb_dlist_node_get_prev_cast( ac_macro_this ) \
  ( (void*)ac_mb_dlist_node_get_prev( (ac_dlist_node_t*)(ac_macro_this) ) )




#ifdef __cplusplus
}
#endif


#endif