/*****************************************************************************

 @(#) bufq.h,v 0.7.8.1 2001/12/11 13:15:00 brian Exp

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 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; either version 2 of the License, or (at your option) any later
 version.

 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 along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified 2001/12/11 13:15:00 by brian

 *****************************************************************************/

#ifndef __BUFQ_H__
#define __BUFQ_H__

typedef struct bufq {
    spinlock_t  q_lock;
    mblk_t      *q_head;
    mblk_t	*q_tail;
    size_t      q_msgs;
    size_t      q_count;
} bufq_t;

static inline void bufq_init(bufq_t *q)
{
    spin_lock_init(&q->q_lock);
    q->q_head  = NULL;
    q->q_tail  = NULL;
    q->q_msgs  = 0;
    q->q_count = 0;
}

static inline int bufq_length(bufq_t *q)
{
    return q->q_msgs;
}

static inline int bufq_size(bufq_t *q)
{
    return q->q_count;
}

static inline mblk_t *bufq_head(bufq_t *q)
{
    return q->q_head;
}

static inline mblk_t *bufq_tail(bufq_t *q)
{
    return q->q_tail;
}

static inline void __bufq_add(bufq_t *q, mblk_t *mp)
{
    mblk_t *md = mp;
    q->q_msgs++;
    while ( md )
    {
	if ( md->b_wptr > md->b_rptr )
	    q->q_count += md->b_wptr - md->b_rptr;
	md = md->b_cont;
    }
    assure( q->q_head );
    assure( q->q_tail );
    assure( q->q_msgs != 1 || !q->q_head->b_next );
    assure( q->q_msgs != 1 || !q->q_tail->b_prev );
}

static inline void __bufq_sub(bufq_t *q, mblk_t *mp)
{
    mblk_t *md = mp;
    while ( md )
    {
	if ( md->b_wptr > md->b_rptr )
	    q->q_count -= md->b_wptr - md->b_rptr;
	md = md->b_cont;
    }
    q->q_msgs--;
    assure( q->q_msgs || !q->q_head );
    assure( q->q_msgs || !q->q_tail );
}

static inline void bufq_queue(bufq_t *q, mblk_t *mp)
{
    ensure( q && mp, return );
    if ( (mp->b_prev = q->q_tail) )
	mp->b_prev->b_next = mp;
    else
	q->q_head = mp;
    mp->b_next = NULL;
    q->q_tail = mp;
    __bufq_add(q, mp);
}

static inline void bufq_queue_head(bufq_t *q, mblk_t *mp)
{
    ensure( q && mp, return );
    if ( (mp->b_next = q->q_head) )
	mp->b_next->b_prev = mp;
    else
	q->q_tail = mp;
    mp->b_prev = NULL;
    q->q_head = mp;
    __bufq_add(q, mp);
}

static inline void bufq_insert(bufq_t *q, mblk_t *mp, mblk_t *np)
{
    ensure( q && mp && np, return );
    if ( (np->b_prev = mp->b_prev) )
	np->b_prev->b_next = np;
    else
	q->q_head = np;
    mp->b_prev = np;
    np->b_next = mp;
    __bufq_add(q, np);
}

static inline void bufq_append(bufq_t *q, mblk_t *mp, mblk_t *np)
{
    ensure( q && mp && np, return );
    if ( (np->b_next = mp->b_next) )
	np->b_next->b_prev = np;
    else
	q->q_tail = np;
    mp->b_next = np;
    np->b_prev = mp;
    __bufq_add(q, np);
}

static inline mblk_t *bufq_dequeue(bufq_t *q)
{
    mblk_t *mp;
    ensure( q, return(NULL) );
    if ( (mp = q->q_head) )
    {
	if ( (q->q_head = mp->b_next) )
	    mp->b_next->b_prev = NULL;
	else
	    q->q_tail  = NULL;
	mp->b_next = NULL;
	mp->b_prev = NULL;
	__bufq_sub(q, mp);
    }
    return mp;
}

static inline mblk_t *bufq_unlink(bufq_t *q, mblk_t *mp)
{
    ensure( q && mp, return(NULL) );
    if ( mp->b_next )
	mp->b_next->b_prev = mp->b_prev;
    else
	q->q_tail = mp->b_prev;
    if ( mp->b_prev )
	mp->b_prev->b_next = mp->b_next;
    else
	q->q_head = mp->b_next;
    mp->b_next = NULL;
    mp->b_prev = NULL;
    __bufq_sub(q, mp);
    return(mp);
}

static inline void bufq_freehead(bufq_t *q)
{
    if ( q->q_head )
	freemsg(bufq_dequeue(q));
}

static inline void bufq_purge(bufq_t *q)
{
    while ( q->q_head )
	freemsg(bufq_dequeue(q));
}

static inline void bufq_supply(bufq_t *q, mblk_t *mp)
{
    mblk_t *md = mp;
    while ( md )
    {
	md->b_datap->db_type = M_DATA;
	md->b_rptr = md->b_wptr = md->b_datap->db_base;
	bufq_queue(q, md);
	md = unlinkb(md);
    }
}

static inline mblk_t *bufq_resupply(bufq_t *q, mblk_t *mp, int maxsize, int maxcount)
{
    if ( bufq_length(q) > maxcount || bufq_size(q) > maxsize )
	return mp;
    bufq_supply(q, mp);
    return NULL;
}

static inline void freechunks(mblk_t *mp)
{
	mblk_t *dp, *dp_next;
	for ( dp = mp; dp; dp_next = dp->b_next, freemsg(dp), dp = dp_next );
}

#endif  __BUFQ_H__
