
NameSpace System
NameSpace IO

Class MemoryStream
	Inherits Stream

Public
	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを初期化します。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream()
		This.writable = True
		This.resizable = True
		This.visible = False
		This.create()
		This.pointer = This.allocate(0)
		This.streamLength = 0
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを初期容量を指定して初期化します。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(capacity As Long)
		This.writable = True
		This.resizable = True
		This.visible = False
		This.create()
		This.pointer = This.allocate(capacity)
		This.streamLength = capacity
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを指定したバッファに基づいて初期化します。容量を変更することはできません。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(buffer As *Byte, length As Long)
		This.writable = True
		This.resizable = False
		This.visible = False
		This.create()
		This.pointer = This.allocate(length)
		MoveMemory(This.pointer,buffer,length)
		This.streamLength = length
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを指定したバッファに基づいて初期化します。容量を変更することはできません。また、書き込みをサポートするかどうかも指定できます。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(buffer As *Byte, length As Long, writable As Boolean)
		This.writable = writable
		This.resizable = False
		This.visible = False
		This.create()
		This.pointer = This.allocate(length)
		MoveMemory(This.pointer,buffer,length)
		This.streamLength = length
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを指定したバッファの指定された領域に基づいて初期化します。容量を変更することはできません。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(buffer As *Byte, index As Long, count As Long)
		This.writable = True
		This.resizable = False
		This.visible = False
		This.create()
		This.pointer = This.allocate(count)
		MoveMemory(This.pointer,VarPtr(buffer[index]),count)
		This.streamLength = count
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを指定したバッファの指定された領域に基づいて初期化します。容量を変更することはできません。また、書き込みをサポートするかどうかを指定できます。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(buffer As *Byte, index As Long, count As Long, writable As Boolean)
		This.writable = writable
		This.resizable = False
		This.visible = False
		This.create()
		This.pointer = This.allocate(count)
		MoveMemory(This.pointer,VarPtr(buffer[index]),count)
		This.streamLength = count
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスの新しいインスタンスを指定したバッファの指定された領域に基づいて初期化します。容量を変更することはできません。また、書き込みをサポートするかどうかを指定できます。GetBufferメソッドをサポートするかどうかを指定できます。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub MemoryStream(buffer As *Byte, index As Long, count As Long, writable As Boolean, visible As Boolean)
		This.writable = writable
		This.resizable = False
		This.visible = visible
		This.create()
		This.pointer = This.allocate(count)
		MoveMemory(This.pointer,VarPtr(buffer[index]),count)
		This.streamLength = count
		This.currentPosition = 0
	End Sub

	/*!
	@brief	MemoryStreamクラスのデストラクタです。
	@author	NoWest
	@date	2008/3/12
	*/
	Sub ~MemoryStream()
		This.Close()
	End Sub

Public
	/*!
	@brief	ストリームが読み取りをサポートしているかどうかを示す値を取得します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function CanRead() As Boolean
		If This.pointer Then
			Return True
		Else
			Return False
		End If
	End Function

	/*!
	@brief	ストリームがシークをサポートしているかどうかを示す値を取得します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function CanSeek() As Boolean
		If This.pointer Then
			Return True
		Else
			Return False
		End If
	End Function

	/*!
	@brief	ストリームが書き込みをサポートしているかどうかを示す値を取得します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function CanWrite() As Boolean
		If This.pointer Then
			Return This.writable
		Else
			Return False
		End If
	End Function

	/*!
	@brief	ストリームに割り当てるメモリ容量をバイト単位で設定します。
	@author	NoWest
	@date	2008/3/12
	*/
	Virtual Sub Capacity(value As Long)
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If value < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Capacity: A capacity is set to negative value.", New System.UInt64(value),"Capacity")
		If value < This.streamLength Then Throw New ArgumentOutOfRangeException("MemoryStream.Capacity: A capacity is less than the current length of the stream.",New System.UInt64(value),"Capacity")
		This.pointer = This.reallocate(This.pointer,value)
	End Sub

	/*!
	@brief	ストリームに割り当てられたメモリ容量をバイト単位で取得します。
	@author	NoWest
	@date	2008/3/12
	*/
	Virtual Function Capacity() As Long
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		Return This.size(This.pointer) As Long
	End Function

	/*!
	@brief	ストリームの長さをバイト単位で取得します。Capacityプロパティの値より小さい場合があります。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function Length() As QWord
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		Return This.streamLength
	End Function

	/*!
	@brief	ストリームの現在位置を設定します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Sub Position(value As QWord)
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If value < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Position: The position is set to a negative value.", New System.UInt64(value), "Position")
		If value > Int32.MaxValue() Then Throw New ArgumentOutOfRangeException("MemoryStream.Position: The position is a value greater than MaxValue.", New System.UInt64(value), "Position")
		This.currentPosition = value As Long
	End Sub

	/*!
	@brief	ストリームの現在位置を取得します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function Position() As QWord
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		Return This.currentPosition As QWord
	End Function

	Override Sub Flush()
	End Sub

/*	Virtual Function GetBuffer() As Array<Byte>
		If visible = False Then Return NULL
		Return p
	End Function*/

	/*!
	@brief	ストリームから指定されたバイト数を読み取り、bufferに書き込みます。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function Read(buffer As *Byte, offset As Long, count As Long) As Long
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If buffer = 0 Then Throw New ArgumentNullException("FileStream.Read: An argument is a null value.", "buffer")
		If offset < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Read: An argument is a negative value.", New System.UInt64(offset), "offset")
		If count < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Read: An argument is a negative value.", New System.UInt64(count), "count")
		If (This.streamLength - (This.currentPosition + count)) < 0 Then
			count + = (This.streamLength - (This.currentPosition + count))
		ElseIf count < 1 Then
			Return 0
		End If
		MoveMemory(VarPtr(buffer[offset]),VarPtr(This.pointer[This.currentPosition]),count)
		This.currentPosition + = count
		Return count
	End Function

	/*!
	@brief	ストリームから指定された1バイト読み取ります。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function ReadByte() As Long
		Dim b As Byte
		Dim ret = Read(VarPtr(b), 0, 1)
		If ret <> 0 Then
			Return b
		Else
			Return -1
		End If
	End Function

	/*!
	@brief	ストリーム内の位置を指定した値に設定します。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Function Seek(offset As Int64, origin As SeekOrigin) As Int64
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If offset > Int32.MaxValue() Then Throw New ArgumentOutOfRangeException("MemoryStream.Seek: The offset is a value greater than MaxValue.", New System.UInt64(offset), "offset")
		Select Case origin
			Case SeekOrigin.Begin
				If offset < 0 Then Throw New IOException("MemoryStream.Seek: Seeking is attempted before the beginning of the stream.")
				This.currentPosition = offset As Long
			Case SeekOrigin.Current
				Beep(440,10)
				If (This.currentPosition + offset) < 0 Then Throw New IOException("MemoryStream.Seek: Seeking is attempted before the beginning of the stream.")
				This.currentPosition += offset As Long
			Case SeekOrigin.End
				If (This.streamLength + offset) < 0 Then Throw New IOException("MemoryStream.Seek: Seeking is attempted before the beginning of the stream.")
				This.currentPosition = (This.streamLength + offset) As Long
			Case Else
				Throw New ArgumentException("MemoryStream.Seek: An argument is an invalid SeekOrigin","origin")
		End Select
		Return This.currentPosition As QWord
	End Function

	/*!
	@brief	ストリームの長さを設定します。メモリ容量を超えては設定できません。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Sub SetLength(value As QWord)
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If This.writable = False Then Throw New NotSupportedException("MemoryStream: The current stream is not writable")
		If This.resizable = False Then Throw New NotSupportedException("MemoryStream: The current stream is not resizable")
		If value > This.Capacity() Then Throw New NotSupportedException("MemoryStream.SetLength: This stream length is larger than the current capacity.")
		If value < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Read: An argument is a negative value.", New System.UInt64(value), "value")
		If value > Int32.MaxValue() Then Throw New ArgumentOutOfRangeException("MemoryStream.SetLength: The length is a value greater than MaxValue.", New System.UInt64(value), "value")
		This.pointer = This.reallocate(This.pointer,value As Long)
		This.streamLength = value As Long
	End Sub

	/*!
	@brief	ストリームにbufferの内容を指定したバイト数書き込みます。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Sub Write(buffer As *Byte, offset As Long, count As Long)
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If This.writable = False Then Throw New NotSupportedException("MemoryStream: The current stream is not writable")
		If buffer = 0 Then Throw New ArgumentNullException("MemoryStream.Write: An argument is a null value.", "buffer")
		If offset < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Write: An argument is a negative value.", New System.UInt64(offset), "offset")
		If count < 0 Then Throw New ArgumentOutOfRangeException("MemoryStream.Write: An argument is a negative value.", New System.UInt64(count), "count")
		If count > (This.streamLength - This.currentPosition) Then
			If This.resizable = False Then Throw New NotSupportedException("MemoryStream: The current stream is not resizable")
			If (This.Capacity() - This.currentPosition) < count Then
				This.pointer = This.reallocate(This.pointer,This.currentPosition+count)
			End If
			This.streamLength = This.currentPosition+count
		End If
		MoveMemory(VarPtr(This.pointer[This.currentPosition]),VarPtr(buffer[offset]),count)
		This.currentPosition + = count
	End Sub

	/*!
	@brief	ストリームに1バイト書き込みます。
	@author	NoWest
	@date	2008/3/12
	*/
	Override Sub WriteByte(b As Byte)
		Write(VarPtr(b), 0, 1)
	End Sub

/*	Virtual Function ToArray() As Array<Byte>
		TODO:
	End Function*/

	Function ToPointer() As VoidPtr
		ToPointer = pointer
	End Function

	/*!
	@brief	ストリームの内容全体をまるごと別のストリームに書き込みます。
	@author	NoWest
	@date	2008/3/12
	*/
	Virtual Sub WriteTo(stream As Stream)
		If This.pointer = 0 Then Throw New ObjectDisposedException("MemoryStream: This stream has closed.")
		If ActiveBasic.IsNothing(stream) Then Throw New ArgumentNullException("MemoryStream.WriteTo: An argument is a null value.", "stream")
		stream.Write(This.pointer,0,This.Capacity())
	End Sub

Protected

	/*!
	@brief	Streamクラスから継承
	@author	NoWest
	@date	2008/3/12
	*/
	Override Sub Dispose(disposing As Boolean)
		This.destroy()
	End Sub

Private
	/* Heap memory address */
	pointer As *Byte
	/* MemoryStream status */
	writable As Boolean
	resizable As Boolean
	visible As Boolean
	streamLength As Long
	currentPosition As Long

	/*
	バッファ管理用のPrivate関数類
	一応初期設定ではGC_mallocでメモリを確保しています。
	*/
	handle As HANDLE
	Sub create()
#ifdef NOT_USE_GC
		This.handle = HeapCreate(0,0,0)
#endif
	End Sub

	Sub destroy()
#ifdef NOT_USE_GC
		HeapFree(This.handle,0,InterlockedExchangePointer(ByVal VarPtr(This.pointer) As VoidPtr,NULL) As VoidPtr)
		HeapDestroy(InterlockedExchangePointer(ByVal VarPtr(This.handle) As VoidPtr,NULL) As HANDLE)
#endif
	End Sub

	Function allocate(length As SIZE_T) As VoidPtr
#ifdef NOT_USE_GC
		Return HeapAlloc(This.handle,HEAP_ZERO_MEMORY,length)
#else
		Return GC_malloc_atomic(length)
#endif
	End Function

	Function reallocate(p As VoidPtr, length As SIZE_T) As VoidPtr
#ifdef NOT_USE_GC
		Return HeapReAlloc(This.handle,HEAP_ZERO_MEMORY,p,This.currentPosition+count)
#else
		Return _System_pGC->__realloc(p,length)
#endif
	End Function

	Function size(p As VoidPtr) As SIZE_T
#ifdef NOT_USE_GC
		Return HeapSize(This.handle,0,p)
#else
		Dim pmemobj = _System_pGC->GetMemoryObjectPtr(p)
		Return pmemobj->size
#endif
	End Function
End Class

End NameSpace 'IO
End NameSpace 'System
