using System;
using System.IO;
namespace LumiSoft.Data.lsDB
{
/// <summary>
/// Data page.
/// </summary>
internal class DataPage
{
private DbFile m_pOwnerDB = null;
private long m_StartPointer = 0;
private bool m_Used = false;
private long m_OwnerID = 0;
private long m_OwnerDataPagePointer = 0;
private long m_NextDataPagePointer = 0;
private int m_DataAreaSize = 1000;
private int m_StoredDataLength = 0;
private byte[] m_Data = null;
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="dataPageDataAreaSize">Specifies how much data data page can store.</param>
/// <param name="ownerDB">Owner DB file..</param>
/// <param name="startOffset">Data page start offset pointer.</param>
public DataPage(int dataPageDataAreaSize,DbFile ownerDB,long startOffset)
{
/* DataPage structure
2 bytes - CRLF
1 byte - used (f - unused,u - used)
8 byte - owner object id
8 bytes - owner data page pointer
8 bytes - continuing data page pointer
4 bytes - stored data length in data area
2 bytes - CRLF
1000 bytes - data area
*/
m_DataAreaSize = dataPageDataAreaSize;
m_pOwnerDB = ownerDB;
m_StartPointer = startOffset;
byte[] dataPageInfo = new byte[33];
ownerDB.SetFilePosition(startOffset);
ownerDB.ReadFromFile(dataPageInfo,0,dataPageInfo.Length);
m_Data = new byte[dataPageDataAreaSize];
ownerDB.ReadFromFile(m_Data,0,dataPageDataAreaSize);
// CRLF
if(dataPageInfo[0] != (byte)'\r'){
throw new Exception("Not right data page startOffset, or invalid data page <CR> is expected but is '" + (int)dataPageInfo[0] + "' !");
}
if(dataPageInfo[1] != (byte)'\n'){
throw new Exception("Not right data page startOffset, or invalid data page <LF> is expected but is '" + (int)dataPageInfo[1] + "' !");
}
// used
if(dataPageInfo[2] == (byte)'u'){
m_Used = true;
}
else{
m_Used = false;
}
// owner object id
m_OwnerID = ldb_Utils.ByteToLong(dataPageInfo,3);
// owner data page pointer
m_OwnerDataPagePointer = ldb_Utils.ByteToLong(dataPageInfo,11);
// continuing data page pointer
m_NextDataPagePointer = ldb_Utils.ByteToLong(dataPageInfo,19);
// stored data length in data area
m_StoredDataLength = ldb_Utils.ByteToInt(dataPageInfo,27);
// CRLF
if(dataPageInfo[31] != (byte)'\r'){
throw new Exception("Not right data page startOffset, or invalid data page <CR> is expected but is '" + (int)dataPageInfo[31] + "' !");
}
if(dataPageInfo[32] != (byte)'\n'){
throw new Exception("Not right data page startOffset, or invalid data page <LF> is expected but is '" + (int)dataPageInfo[32] + "' !");
}
}
#region static method CreateDataPage
/// <summary>
/// Creates new data page structure.
/// </summary>
/// <param name="dataPageDataAreaSize">Specifies how much data can data page store.</param>
/// <param name="used">Specifies if data page is used or free space. If this value is false, all toher parameters aren't stored.</param>
/// <param name="ownerID">Owner data object ID.</param>
/// <param name="ownerDataPagePointer">This data page owner data page pointer. This value can be 0, if no owner.</param>
/// <param name="nextDataPagePointer">Data page pointer, what continues this data page. This value can be 0 if, data page won't spread to multiple data pages.</param>
/// <param name="data">Data what data page stores. Maximum length is dataPageDataAreaSize.</param>
/// <returns></returns>
public static byte[] CreateDataPage(int dataPageDataAreaSize,bool used,long ownerID,long ownerDataPagePointer,long nextDataPagePointer,byte[] data)
{
/* DataPage structure
2 bytes - CRLF
1 byte - used (f - unused,u - used)
8 byte - owner object id
8 bytes - owner data page pointer
8 bytes - continuing data page pointer
4 bytes - stored data length in data area
2 bytes - CRLF
dataPageDataAreaSize bytes - data area
*/
if(data.Length > dataPageDataAreaSize){
throw new Exception("Data page can store only " + dataPageDataAreaSize + " bytes, data conatins '" + data.Length + "' bytes !");
}
byte[] dataPage = new byte[dataPageDataAreaSize + 33];
// CRLF
dataPage[0] = (byte)'\r';
dataPage[1] = (byte)'\n';
if(used){
// used
dataPage[2] = (byte)'u';
// owner object id
Array.Copy(ldb_Utils.LongToByte(ownerID),0,dataPage,3,8);
// owner data page pointer
Array.Copy(ldb_Utils.LongToByte(ownerDataPagePointer),0,dataPage,11,8);
// continuing data page pointer
Array.Copy(ldb_Utils.LongToByte(nextDataPagePointer),0,dataPage,19,8);
// stored data length in data area
Array.Copy(ldb_Utils.IntToByte(data.Length),0,dataPage,27,4);
// CRLF
dataPage[31] = (byte)'\r';
dataPage[32] = (byte)'\n';
// data area
Array.Copy(data,0,dataPage,33,data.Length);
}
else{
// used
dataPage[2] = (byte)'f';
// CRLF
dataPage[31] = (byte)'\r';
dataPage[32] = (byte)'\n';
}
return dataPage;
}
#endregion
#region method ReadData
/// <summary>
/// Reads specified amount data to buffer.
/// </summary>
/// <param name="buffer">Buffer where to store data.</param>
/// <param name="startIndexInBuffer">Start index in buffer where data storing begins. Start index is included.</param>
/// <param name="length">Number of bytes to read.</param>
/// <param name="startOffset">Zero based offset of data area.</param>
/// <returns></returns>
public void ReadData(byte[] buffer,int startIndexInBuffer,int length,int startOffset)
{
if(startOffset < 0){
throw new Exception("startOffset can't negative value !");
}
if((length + startOffset) > this.DataAreaSize){
throw new Exception("startOffset and length are out of range data page data area !");
}
if((length + startOffset) > m_StoredDataLength){
throw new Exception("There isn't so much data stored in data page as requested ! Stored data length = " + m_StoredDataLength + "; start offset = " + startOffset + "; length wanted = " + length);
}
Array.Copy(m_Data,startOffset,buffer,startIndexInBuffer,length);
}
/// <summary>
/// Reads data page data. Offset byte is included.
/// </summary>
/// <param name="startOffset">Zero based offset of data area.</param>
/// <param name="length">Specifies how much data to read.</param>
/// <returns></returns>
public byte[] ReadData(int startOffset,int length)
{
if(startOffset < 0){
throw new Exception("startOffset can't negative value !");
}
if((length + startOffset) > this.DataAreaSize){
throw new Exception("startOffset and length are out of range data page data area !");
}
if((length + startOffset) > m_StoredDataLength){
throw new Exception("There isn't so much data stored in data page as requested ! Stored data length = " + m_StoredDataLength + "; start offset = " + startOffset + "; length wanted = " + length);
}
byte[] data = new byte[length];
Array.Copy(m_Data,startOffset,data,0,length);
return data;
}
#endregion
#region method WriteData
/// <summary>
/// Writed data to data page.
/// </summary>
/// <param name="data">Data to write.</param>
public void WriteData(byte[] data)
{
if(data.Length > this.DataAreaSize){
throw new Exception("Data page can't store more than " + this.DataAreaSize + " bytes, use mutliple data pages !");
}
// Set stored data length
m_pOwnerDB.SetFilePosition(m_StartPointer + 27);
m_pOwnerDB.WriteToFile(ldb_Utils.IntToByte(data.Length),0,4);
// Store data
m_pOwnerDB.SetFilePosition(m_StartPointer + 33);
m_pOwnerDB.WriteToFile(data,0,data.Length);
m_StoredDataLength = data.Length;
}
#endregion
#region Properties Implementation
/// <summary>
/// Gets data page size on disk in bytes.
/// </summary>
public int DataPageSize
{
get{ return 33 + this.DataAreaSize; }
}
/// <summary>
/// Gets this data page address (offset in database file).
/// </summary>
public long Pointer
{
get{ return m_StartPointer; }
}
/// <summary>
/// Gets or sets if data page used or free space.
/// </summary>
public bool Used
{
get{ return m_Used; }
set{
m_pOwnerDB.SetFilePosition(m_StartPointer + 2);
m_pOwnerDB.WriteToFile(new byte[]{Convert.ToByte(value)},0,1);
}
}
/// <summary>
/// Gets owner object id what owns this data page.
/// </summary>
public long OwnerID
{
get{ return m_OwnerID; }
}
/// <summary>
/// Gets or sets owner data page pointer.
/// Returns 0 if this is first data page of multiple data pages or only data page.
/// </summary>
public long OwnerDataPagePointer
{
get{ return m_OwnerDataPagePointer; }
set{
// owner data page pointer
m_pOwnerDB.SetFilePosition(m_StartPointer + 11);
m_pOwnerDB.WriteToFile(ldb_Utils.LongToByte(value),0,8);
m_OwnerDataPagePointer = value;
}
}
/// <summary>
/// Gets or sets pointer to data page what continues this data page.
/// Returns 0 if data page has enough room for data and there isn't continuing data page.
/// </summary>
public long NextDataPagePointer
{
get{ return m_NextDataPagePointer; }
set{
// continuing data page pointer
m_pOwnerDB.SetFilePosition(m_StartPointer + 19);
m_pOwnerDB.WriteToFile(ldb_Utils.LongToByte(value),0,8);
m_NextDataPagePointer = value;
}
}
/*
/// <summary>
/// Gets or sets data that data page holds. Maximum size is this.DataAreaSize. Returns null if no data stored.
/// </summary>
public byte[] Data
{
get{
byte[] data = new byte[m_StoredDataLength];
m_pDbFileStream.Position = m_StartPointer + 33;
m_pDbFileStream.Read(data,0,data.Length);
return data;
}
set{
if(value.Length > this.DataAreaSize){
throw new Exception("Data page can't store more than " + this.DataAreaSize + " bytes, use mutliple data pages !");
}
// Set stored data length
m_pDbFileStream.Position = m_StartPointer + 27;
byte[] dataLength = ldb_Utils.IntToByte(value.Length);
m_pDbFileStream.Write(dataLength,0,dataLength.Length);
// Store data
m_pDbFileStream.Position = m_StartPointer + 33;
m_pDbFileStream.Write(value,0,value.Length);
m_StoredDataLength = value.Length;
}
}
*/
/// <summary>
/// Gets how many data data page can store.
/// </summary>
public int DataAreaSize
{
get{ return m_DataAreaSize; }
}
/// <summary>
/// Gets stored data length.
/// </summary>
public int StoredDataLength
{
get{ return m_StoredDataLength; }
}
/// <summary>
/// Gets how much free data space is availabe in data page.
/// </summary>
public long SpaceAvailable
{
get{ return this.DataAreaSize - m_StoredDataLength; }
}
#endregion
}
}