/* mbuffer.h
 *
 * Author       Karsten Keil <kkeil@linux-pingi.de>
 *
 * Copyright 2007  by Karsten Keil <kkeil@novell.com>
 * Copyright 2011  by Karsten Keil <kkeil@linux-pingi.de>
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
 * version 2.1 as published by the Free Software Foundation.
 *
 * This code 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 LESSER GENERAL PUBLIC LICENSE for more details.
 *
 */
              
#ifndef _MBUFFER_H
#define _MBUFFER_H

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>
#include <mISDN/mISDNif.h>
#include <mISDN/mlayer3.h>

#ifndef eprint
#define eprint(fmt, ...)	mi_printf(__FILE__, __LINE__, __PRETTY_FUNCTION__, 1, fmt, ##__VA_ARGS__)
extern int mi_printf(const char *file, int line, const char *func, int lev, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
#endif

struct mqueue {
	struct mbuffer	*prev;
	struct mbuffer	*next;
	pthread_mutex_t	lock;
	int		len;
};

#ifdef MEMLEAK_DEBUG
struct lhead {
	struct lhead *prev;
	struct lhead *next;
};
#endif

struct mbuffer {
	struct mbuffer		*prev;
	struct mbuffer		*next;
	struct mqueue		*list;
	int			refcnt;
	struct mISDNhead	*h;
	struct sockaddr_mISDN	addr;
	unsigned char		*head;
	unsigned char		*data;
	unsigned char		*tail;
	unsigned char		*end;
	unsigned char		*chead;
	unsigned char		*ctail;
	unsigned char		*cend;
	int			len;
	struct l3_head		l3h;
	struct l3_msg		l3;
#ifdef MEMLEAK_DEBUG
	struct lhead 		Alist;
	char			d_fn[80];
	int			d_ln;
#endif
} __attribute__((__may_alias__));

#define	MBUFFER_DATA_SIZE	280

/*
 * init mbuffer caching
 * @parameter count of cached mbuffers
 */
extern void		init_mbuffer(int);

/*
 * free the cache
 */
extern void		cleanup_mbuffer(void);

#ifdef MEMLEAK_DEBUG
/*
 * alloc a new mbuffer
 */

extern struct mbuffer	*__alloc_mbuffer(const char *file, int lineno, const char *func);

/*
 * free the message
 */
extern void		__free_mbuffer(struct mbuffer *, const char *file, int lineno, const char *func);

#define alloc_mbuffer()	__alloc_mbuffer(__FILE__, __LINE__, __PRETTY_FUNCTION__)
#define free_mbuffer(p)	__free_mbuffer(p, __FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
/*
 * alloc a new mbuffer
 */
extern struct mbuffer	*alloc_mbuffer(void);

/*
 * free the message 
 */
extern void		free_mbuffer(struct mbuffer *);
#endif

static inline void
mqueue_init(struct mqueue *q)
{
	pthread_mutex_init(&q->lock, NULL);
	q->len = 0;
	q->prev = (struct mbuffer *)q;
	q->next = (struct mbuffer *)q;
}

static inline int mqueue_len(struct mqueue *q)
{
	return(q->len);
}

static inline void mqueue_head(struct mqueue *q, struct mbuffer *newm)
{
	pthread_mutex_lock(&q->lock);
	newm->list = q;
	q->len++;
	q->next->prev = newm;
	newm->next = q->next;
	newm->prev = (struct mbuffer *)q;
	q->next = newm;
	pthread_mutex_unlock(&q->lock);
}


static inline void mqueue_tail(struct mqueue *q, struct mbuffer *newm)
{
	pthread_mutex_lock(&q->lock);
	newm->list = q;
	q->len++;
	q->prev->next = newm;
	newm->next = (struct mbuffer *)q;;
	newm->prev = q->prev;
	q->prev = newm;
	pthread_mutex_unlock(&q->lock);
}


static inline struct mbuffer *mdequeue(struct mqueue *q)
{
	struct mbuffer *next, *prev, *result;

	pthread_mutex_lock(&q->lock);
	prev = (struct mbuffer *)q;
	next = prev->next;
	result = NULL;
	if (next != prev) {
		result = next;
		next = next->next;
		q->len--;
		next->prev = prev;
		prev->next = next;
		result->list = NULL;
	}
	pthread_mutex_unlock(&q->lock);
	return result;
}

static __inline__ void mqueue_purge(struct mqueue *q)
{
	struct mbuffer *mb;

	while ((mb = mdequeue(q))!=NULL)
		free_mbuffer(mb);
}

static __inline__ unsigned char *msg_put(struct mbuffer *msg, unsigned int len)
{
	unsigned char *tmp = msg->tail;

	msg->tail += len;
	msg->len += len;
	if (msg->tail > msg->end) {
		eprint("msg_over_panic msg(%p) data(%p) head(%p)\n",
			msg, msg->data, msg->head);
		return NULL;
	}
	return tmp;
}

static __inline__ unsigned char *msg_push(struct mbuffer *msg, unsigned int len)
{
	msg->data -= len;
	msg->len += len;
	if(msg->data < msg->head)
	{
		eprint("msg_under_panic msg(%p) data(%p) head(%p)\n",
			msg, msg->data, msg->head);
		return NULL;
	}
	return msg->data;
}


static __inline__ unsigned char *__msg_pull(struct mbuffer *msg, unsigned int len)
{
	unsigned char *tmp = msg->data;

	msg->len -= len;
	msg->data += len;
	return tmp;
}

static __inline__ unsigned char * msg_pull(struct mbuffer *msg, unsigned int len)
{
	if (len > (unsigned int)msg->len)
		return NULL;
	return (unsigned char *)__msg_pull(msg,len);
}

static __inline__ int msg_headroom(struct mbuffer *msg)
{
	return msg->data - msg->head;
}

static __inline__ int msg_tailroom(struct mbuffer *msg)
{
	return msg->end-msg->tail;
}

static __inline__ void msg_reserve(struct mbuffer *msg, unsigned int len)
{
	msg->data += len;
	msg->tail += len;
}

static __inline__ void __msg_trim(struct mbuffer *msg, unsigned int len)
{
	msg->len = len;
	msg->tail = msg->data + len;
}

static __inline__ void msg_trim(struct mbuffer *msg, unsigned int len)
{
	if ((unsigned int)msg->len > len) {
		__msg_trim(msg, len);
	}
}

#ifdef __cplusplus
}
#endif

#endif
