/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 :
 * =====================================================================
 *  sc_stream_stream.c
 *  Copyright (c)  2003 - 2011  sys0tem
 *  LICENSE :
 *	LGPL (GNU Lesser General Public License - Version 3,29 June 2007)
 *	http://www.gnu.org/copyleft/lesser.html
 *	or
 *	EPL (Eclipse Public License - v1.0)
 *	http://www.eclipse.org/legal/epl-v10.html
 * =====================================================================
 */
#include <string.h>
#include <sc_os.h>
#include <sc_error.h>
#include <sc_mmgr.h>
#include <sc_stream_socket.h>


typedef struct {
	socket_t sockfd;
} SC_Stream_SocketInfo;


/* ---------------------------------------------------------------------
 *  プロトタイプ宣言
 * ---------------------------------------------------------------------
 */
static int  SC_Stream_socketRead(SC_Stream* strm, void* buff, size_t size);
static int  SC_Stream_socketWrite(SC_Stream* strm, const void* buff, size_t size);
static bool SC_Stream_socketClose(SC_Stream* strm);

/**
 * ソケットストリームを構築します。
 * 構築に失敗した場合、NULL を返します。
 *
 * @param fp       ファイルディスクリプタ
 * @param buffSize 読み取りバッファサイズ
 * @return ストリーム
 */
SC_Stream* SC_Stream_newSocketStream(socket_t sockfd, size_t buffSize)
{
	SC_Stream*            strm;
	SC_Stream_SocketInfo* info;
	size_t                size;
	size = sizeof(SC_Stream) + buffSize + sizeof(SC_Stream_SocketInfo);
	strm = (SC_Stream*) malloc(size);
	if (strm == NULL)
	{	/* メモリ確保失敗	*/
		return NULL;
	}
	strm->_data.buff       = (char*) (strm + 1);
	strm->_data.buffSize   = buffSize;
	strm->_data.off        = 0;
	strm->_data.size       = 0;

	info = (SC_Stream_SocketInfo*) (strm->_data.buff + strm->_data.buffSize);
	info->sockfd           = sockfd;

	strm->_strmInfo        = info;
	strm->readc            = SC_Stream_readc_;
	strm->read             = SC_Stream_read_;
	strm->readExact        = SC_Stream_readExact_;
	strm->readLine         = SC_Stream_readLine_;
	strm->limitedRead      = SC_Stream_limitedRead_;
	strm->writec           = SC_Stream_writec_;
	strm->write            = SC_Stream_write_;
	strm->writeExact       = SC_Stream_writeExact_;
	strm->_readCore        = SC_Stream_socketRead;
	strm->_writeCore       = SC_Stream_socketWrite;
	strm->close            = SC_Stream_socketClose;
	return strm;
}


/**
 * ソケットからデータを読み込みます。
 * 終了した場合 0 を返します。
 * エラーが発生した場合 -1 を返します。
 *
 * @param strm ストリーム
 * @param buff バッファ
 * @param size サイズ
 * @return 読み取ったバッファサイズ
 */
static
int SC_Stream_socketRead(SC_Stream* strm, void* buff, size_t size)
{
	ssize_t readSize;
	int     sockerr;
	SC_Stream_SocketInfo* info = (SC_Stream_SocketInfo*) strm->_strmInfo;

	do
	{	/* SC_EINTR の場合、再度読み取る	*/
		SC_setError(0);
		sockerr  = 0;
		readSize = recv(info->sockfd, buff, size, 0);
		if (readSize < 0)
		{
			sockerr = SC_getSocketError();
		}
	} while(sockerr == SC_EINTR);

	return readSize;
}


/**
 * ソケットにデータを書き込みます。
 *
 * @param strm ストリーム
 * @param buff バッファ
 * @param size サイズ
 * @return 書き出したバッファサイズ
 */
static
int SC_Stream_socketWrite(SC_Stream* strm, const void* buff, size_t size)
{
	ssize_t writeSize;
	int     sockerr;
	SC_Stream_SocketInfo* info = (SC_Stream_SocketInfo*) strm->_strmInfo;

	do
	{	/* SC_EINTR の場合、再度読み取る	*/
		SC_setError(0);
		sockerr  = 0;
		writeSize = send(info->sockfd, buff, size, 0);
		if (writeSize < 0)
		{
			sockerr = SC_getSocketError();
		}
	} while(sockerr == SC_EINTR);

	return writeSize;
}


/**
 * ストリームを close します。
 * close に失敗した場合、false を返します。
 *
 * @param strm ストリーム
 */
static
bool SC_Stream_socketClose(SC_Stream* strm)
{
	SC_Stream_SocketInfo* info;
	bool                  ret;

	if (strm->_strmInfo == NULL) { return false; }
	info = (SC_Stream_SocketInfo*) strm->_strmInfo;

	ret = SC_Socket_close(info->sockfd);
	return ret;
}

