/* vim: ts=4 sw=4 sts=4 fenc=utf-8 ff=unix :
 * =============================================================================
 *  binary
 *
 *  Copyright (C) 2008 by kei-n
 *  URL : http://sys0tem.homelinux.com/
 *
 * =============================================================================
 */
module sal.io.binary;


public import std.stream;
private import sal.io.serializable;


/**
 * バッファ無しでファイルシステムのストリームを扱う
 * 派生クラスです.
 */
public
class BinaryFile : File
{
	/**
	 * 1:ファイルを開かず、 
	 * 2:読み取り専用でファイルを開いて、 
	 * 3:モードを明示指定してファイルを開いて、
	 * ストリームを作成します.
	 * mode は FileMode.In（ファイルを読み込めることを示す）と
	 * FileMode.Out（ファイルへ書き込めることを示す）の組み合わせです.
	 * 存在しないファイルを読みとりオープンするとエラーになります.
	 * 存在しないファイルを書き込みオープンすると、新しいファイルが作られます.
	 * FileMode.OutNew は書き込み用にファイルを開き、長さ0に設定します.
	 * FileMode.Append は書き込み用にファイルを開き、
	 * ファイル終端へ位置を移動します.
	 */
	public
	this(string filename, FileMode mode = (FileMode).In)
	{
		super(filename, mode);
	}


	/**
	 * 指定された型で読み込みます.
	 * 本引数に指定された型は、アライメントされず
	 * 本来の型のサイズで読み込まれます.
	 * 正確に読み込めない場合 ReadException を投げます.
	 *
	 * Params:
	 *   t = 読み込みバッファ
	 *   e = エンディアン
	 * Return:
	 *   読み込んだ値
	 */
	public
	T readExact(T)(inout T t, in Endian e = endian)
	{
		size_t  size       = getSize(t);
		ubyte[] byteBuffer = new ubyte[size];
		ubyte*  bytePtr    = byteBuffer.ptr;

		size_t readSize;
		for (;;)
		{
			if (size == 0)
			{	// T 型の分読み込んだ
				break;
			}
			readSize = readBlock(bytePtr, size);
			if (readSize == 0)
			{	// ファイルの終わりまで読み込んだ
				break;
			}
			bytePtr += readSize;
			size    -= readSize;
		}
		if (size != 0)
		{
			throw new ReadException("not enough data in stream");
		}
		// ubyte[] を t に入れる
		return toValue(t, byteBuffer, e);
	}


	/**
	 * 指定された型の値を書き込みます.
	 * 本引数に指定された型は、アライメントされず
	 * 本来の型のサイズで書き込まれます.
	 * 正確に書き込めない場合 WriteException を投げます.
	 *
	 * Params:
	 *   t = 書き込む値
	 *   e = エンディアン
	 */
	public
	void writeExact(T)(in T t, in Endian e = endian)
	{
		// byte 配列に変換
		ubyte[] buff    = toBytes(t, e);
		size_t  size    = buff.length;
		ubyte*  bytePtr = buff.ptr;
		size_t  writeSize;

		for (;;)
		{
			if (size == 0)
			{	// T 型のサイズ分書き込んだ
				return;
			}
			writeSize = writeBlock(bytePtr, size);
			if (writeSize == 0)
			{	// 0 byte 書き込んだ
				break;
			}
			bytePtr += writeSize;
			size    -= writeSize;
		}
		if (size != 0)
		{
			throw new WriteException("unable to write to stream");
		}
	}
}

