/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

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



#include	"lock_level.h"
#include	"task.h"
#include	"memory_debug.h"
#include	"stream.h"
#include	"s_buf.h"

int s_chunk_read(STREAM * s,void * data,int len);

#define COPY_IN_MAX_SIZE	4096



int
en_do(int * ret_len,int (*func)(),STREAM * s,void * data,int len)
{
int er;
char * ptr;
int ret;
	ptr = data;
	ret = 0;
	for ( ; len ; ) {
		er = (*func)(s,ptr,len);
		if ( er < 0 ) {
			*ret_len = ret;
			return -1;
		}
		if ( er == 0 ) {
			*ret_len = ret;
			return 0;
		}
		len -= er;
		ptr += er;
		ret += er;
	}
	*ret_len = ret;
	return 1;
}


void
init_sbuf(STREAM_BUF * b,SEM lock)
{
	if ( lock )
		lock_task(lock);
	b->head = b->tail = 0;
	if ( lock )
		unlock_task(lock,"init_sbuf");
}

void
free_sbuf(STREAM_BUF * b,SEM lock)
{
SBUF_DATA * d;

	if ( lock )
		lock_task(lock);
	for ( ; b->head ; ) {
		d = b->head;
		b->head = d->next;
		d_f_ree(d);
	}
	if ( lock )
		unlock_task(lock,"free_sbuf");
}



void
copy_in_sbuf(STREAM_BUF * b,void * data,int len,SEM lock)
{
SBUF_DATA * d;
	d = d_alloc(sizeof(*d)+len,123);
	memcpy(d+1,data,len);
	d->next = 0;
	d->ptr = 0;
	d->len = len;

	if ( lock )
		lock_task(lock);
	if ( b->head ) {
		b->tail->next = d;
		b->tail = d;
	}
	else	b->head = b->tail = d;
	if ( lock )
		unlock_task(lock,"copy_in_sbuf");
}

int
_copy_in_sbuf_stream(STREAM_BUF * b,STREAM * s,int len,SEM lock)
{
SBUF_DATA * d;
int er;
	d = d_alloc(sizeof(*d)+len,123);
	er = en_do(&d->len,s_chunk_read,s,d+1,len);
	if ( d->len == 0 ) {
		d_f_ree(d);
		return er;
	}
	d->next = 0;
	d->ptr = 0;

	if ( lock )
		lock_task(lock);
	if ( b->head ) {
		b->tail->next = d;
		b->tail = d;
	}
	else	b->head = b->tail = d;
	if ( lock )
		unlock_task(lock,"_copy_in_sbuf_stream");
	if ( d->len > 0 )
		return 1;
	else 	return er;
}

int
copy_in_sbuf_stream(STREAM_BUF * b,STREAM * s,int len,SEM lock)
{
int ret;
	if ( len < 0 ) {
		ret = 1;
		for ( ; ret > 0 ; )
			ret = _copy_in_sbuf_stream(
					b,s,COPY_IN_MAX_SIZE,lock);
		return ret;
	}
	else {
		return _copy_in_sbuf_stream(b,s,len,lock);
	}
}

int
get_sbuf_data_size(STREAM_BUF * b,SEM lock)
{
SBUF_DATA * d;
int ret;

	if ( lock )
		lock_task(lock);
	ret = 0;
	for ( d = b->head ; d ; d = d->next )
		ret += d->len - d->ptr;
	if ( lock )
		unlock_task(lock,"get_sbuf_data_size");
	return ret;
}

int
copy_out_sbuf(STREAM_BUF * b,void * data,int size,SEM lock)
{
int ret;
int len;
SBUF_DATA * d;
char * ptr;

	if ( lock )
		lock_task(lock);
	ptr = data;
	ret = 0;
	for ( ; b->head && size ; ) {
		d = b->head;
		len = d->len - d->ptr;
		if ( len > size ) {
			memcpy(ptr,&((char*)(d+1))[d->ptr],size);
			ret += size;
			d->ptr += size;
			size = 0;
		}
		else {
			memcpy(ptr,&((char*)(d+1))[d->ptr],len);
			ret += len;
			size -= len;
			ptr += len;

			b->head = d->next;
			if ( b->head == 0 )
				b->tail = 0;
		}
	}
	if ( lock )
		unlock_task(lock,"copy_out_sbuf");
	return ret;
}


SBUF_DATA *
get_sbuf_head(STREAM_BUF * b,SEM lock)
{
SBUF_DATA * ret;

	if ( lock )
		lock_task(lock);
	if ( b->head == 0 ) {
		ret = 0;
		goto end;
	}
	ret = b->head;
	b->head = ret->next;
	if ( b->head == 0 )
		b->tail = 0;
end:
	if ( lock )
		unlock_task(lock,"get_sbuf_head");
	return ret;
}


