Click here to Skip to main content
15,881,424 members
Articles / Programming Languages / C#

SIP Stack with SIP Proxy - (VOIP)

Rate me:
Please Sign up or sign in to vote.
4.86/5 (45 votes)
11 Jun 2007CPOL2 min read 1.6M   28.1K   162  
C# implementation of SIP
using System;
using System.Collections;
using System.IO;

using LumiSoft.Net;

namespace LumiSoft.Data.lsDB
{	
	/// <summary>
	/// LumiSoft database file.
	/// </summary>
	public class DbFile : IDisposable
	{
		private LDB_DataColumnCollection m_pColumns             = null;
		private FileStream               m_pDbFile              = null;
		private string                   m_DbFileName           = "";
		private long                     m_DatapagesStartOffset = -1;
		private LDB_Record               m_pCurrentRecord       = null;
		private bool                     m_TableLocked          = false;
		private int                      m_DataPageDataAreaSize = 1000;

		private long                     m_FileLength   = 0;
		private long                     m_FilePosition = 0;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public DbFile()
		{
			m_pColumns = new LDB_DataColumnCollection(this);
		}

		#region method Dispose

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		public void Dispose()
		{
			Close();
		}

		#endregion


		#region method Open

		/// <summary>
		/// Opens specified data file.
		/// </summary>
		/// <param name="fileName">File name.</param>
		public void Open(string fileName)
		{
			Open(fileName,0);
		}

		/// <summary>
		/// Opens specified data file.
		/// </summary>
		/// <param name="fileName">File name.</param>
		/// <param name="waitTime">If data base file is exclusively locked, then how many seconds to wait file to unlock before raising a error.</param>
		public void Open(string fileName,int waitTime)
		{
			DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime);
			while(true){
				try{
					m_pDbFile = File.Open(fileName,FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite);

					break;
				}
				catch(IOException x){
					// Make this because to get rid of "The variable 'x' is declared but never used"
					string dummy = x.Message;

					System.Threading.Thread.Sleep(15);

					// Lock wait time timed out
                    if(DateTime.Now > lockExpireTime){
						throw new Exception("Database file is locked and lock wait time expired !");
					}
				}
			}

			/* Table structure:
				50    bytes         - version
				2     bytes         - CRLF
				8     bytes         - free datapages count
				2     bytes         - CRLF
				4     bytes         - datapage data area size
				2     bytes         - CRLF
				100 x 500 bytes     - 100 columns info store
				2     bytes         - CRLF
				... data pages
			*/

			m_DbFileName = fileName;
			StreamLineReader r = new StreamLineReader(m_pDbFile);

			// TODO: check if LDB file

			// Read version line (50 bytes + CRLF)	
			byte[] version = r.ReadLine();

			// Skip free data pages count
			byte[] freeDataPagesCount = new byte[10];
			m_pDbFile.Read(freeDataPagesCount,0,freeDataPagesCount.Length);

			// 4 bytes datapage data area size + CRLF
			byte[] dataPageDataAreaSize = new byte[6];
			m_pDbFile.Read(dataPageDataAreaSize,0,dataPageDataAreaSize.Length);
			m_DataPageDataAreaSize = ldb_Utils.ByteToInt(dataPageDataAreaSize,0);

			// Read 100 column lines (500 + CRLF bytes each)
			for(int i=0;i<100;i++){
				byte[] columnInfo = r.ReadLine();
				if(columnInfo == null){
					throw new Exception("Invalid columns data area length !");
				}

				if(columnInfo[0] != '\0'){					
					m_pColumns.Parse(columnInfo);
				}
			}

			// Header terminator \0
			m_pDbFile.Position++;
	
			// No we have rows start offset
			m_DatapagesStartOffset = m_pDbFile.Position;

			// Store file length and position
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_pDbFile.Position;
		}

		#endregion

		#region method Close

		/// <summary>
		/// Closes database file.
		/// </summary>
		public void Close()
		{
			if(m_pDbFile != null){
				m_pDbFile.Close();
				m_pDbFile = null;
				m_DbFileName = "";
				m_FileLength = 0;
				m_FilePosition = 0;
			}
		}

		#endregion

		#region method Create

		/// <summary>
		/// Creates new database file.
		/// </summary>
		/// <param name="fileName">File name.</param>
		public void Create(string fileName)
		{
			Create(fileName,1000);
		}

		/// <summary>
		/// Creates new database file.
		/// </summary>
		/// <param name="fileName">File name.</param>
		/// <param name="dataPageDataAreaSize">Specifies how many data can data page store.</param>
		public void Create(string fileName,int dataPageDataAreaSize)
		{			
			m_pDbFile = new FileStream(fileName,FileMode.CreateNew,FileAccess.ReadWrite,FileShare.None);

			/* Table structure:
				50    bytes         - version
				2     bytes         - CRLF
				8     bytes         - free datapages count
				2     bytes         - CRLF
				4     bytes         - datapage data area size
				2     bytes         - CRLF
				100 x 500 bytes     - 100 columns info store
				2     bytes         - CRLF
				... data pages
			*/

			// Version 50 + CRLF bytes
			byte[] versionData = new byte[52];
			versionData[0] = (byte)'1';
			versionData[1] = (byte)'.';
			versionData[2] = (byte)'0';
			versionData[50] = (byte)'\r';
			versionData[51] = (byte)'\n';
			m_pDbFile.Write(versionData,0,versionData.Length);

			// 8 bytes free data pages count + CRLF
			byte[] freeDataPagesCount = new byte[10];
			freeDataPagesCount[8] = (byte)'\r';
			freeDataPagesCount[9] = (byte)'\n';
			m_pDbFile.Write(freeDataPagesCount,0,freeDataPagesCount.Length);

			// 4 bytes datapage data area size + CRLF
			byte[] dataPageDataAreaSizeB = new byte[6];
			Array.Copy(ldb_Utils.IntToByte(dataPageDataAreaSize),0,dataPageDataAreaSizeB,0,4);
			dataPageDataAreaSizeB[4] = (byte)'\r';
			dataPageDataAreaSizeB[5] = (byte)'\n';
			m_pDbFile.Write(dataPageDataAreaSizeB,0,dataPageDataAreaSizeB.Length);
			
			// 100 x 100 + CRLF bytes header lines
			for(int i=0;i<100;i++){
				byte[] data = new byte[100];
				m_pDbFile.Write(data,0,data.Length);
				m_pDbFile.Write(new byte[]{(int)'\r',(int)'\n'},0,2);
			}

			// Headers terminator char(0)
			m_pDbFile.WriteByte((int)'\0');

			// Data pages start pointer
			m_DatapagesStartOffset = m_pDbFile.Position - 1;

			m_DbFileName = fileName;
			m_DataPageDataAreaSize = dataPageDataAreaSize;

			// Store file length and position
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_pDbFile.Position;
		}

		#endregion


		#region method LockTable
		
		/// <summary>
		/// Locks table.
		/// </summary>
		/// <param name="waitTime">If table is locked, then how many sconds to wait table to unlock, before teturning error.</param>
		public void LockTable(int waitTime)
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			// Table is locked already, just skip locking
			if(m_TableLocked){
				return;
			}

			DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime);
			while(true){
				try{
					// We just lock first byte
					m_pDbFile.Lock(0,1);
					m_TableLocked = true;

					break;
				}
				// Catch the IOException generated if the 
				// specified part of the file is locked.
				catch(IOException x){
					// Make this because to get rid of "The variable 'x' is declared but never used"
					string dummy = x.Message;

					System.Threading.Thread.Sleep(15);

					// Lock wait time timed out
                    if(DateTime.Now > lockExpireTime){
						throw new Exception("Table is locked and lock wait time expired !");
					}
				}
			}
		}

		#endregion

		#region method UnlockTable

		/// <summary>
		/// Unlock table.
		/// </summary>
		public void UnlockTable()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}

			if(m_TableLocked){
				// We just unlock first byte
				m_pDbFile.Unlock(0,1);
			}
		}

		#endregion

		#region method LockRecord
/*
		/// <summary>
		/// Locks current record.
		/// </summary>
		public void LockRecord()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}

		}
*/
		#endregion

		#region method UnlockRecord
/*
		/// <summary>
		/// Unlocks current record.
		/// </summary>
		public void UnlockRecord()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}

		}
*/
		#endregion


		#region method NextRecord

		/// <summary>
		/// Gets next record. Returns true if end of file reached and there are no more records.
		/// </summary>
		/// <returns>Returns true if end of file reached and there are no more records.</returns>
		public bool NextRecord()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}

			//--- Find next record ---------------------------------------------------//
			long nextRowStartOffset = 0;
			if(m_pCurrentRecord == null){
				nextRowStartOffset = m_DatapagesStartOffset;
			}
			else{
				nextRowStartOffset = m_pCurrentRecord.DataPage.Pointer + m_DataPageDataAreaSize + 33;
			}

			while(true){
				if(m_FileLength > nextRowStartOffset){
					DataPage dataPage = new DataPage(m_DataPageDataAreaSize,this,nextRowStartOffset);

					// We want datapage with used space
					if(dataPage.Used && dataPage.OwnerDataPagePointer < 1){
						m_pCurrentRecord = new LDB_Record(this,dataPage);
						break;
					}
				}
				else{
					return true;
				}
				
				nextRowStartOffset += m_DataPageDataAreaSize + 33;
			}
			//-------------------------------------------------------------------------//
			
			return false;
		}

		#endregion


		#region method AppendRecord

		/// <summary>
		/// Appends new record to table.
		/// </summary>
		public void AppendRecord(object[] values)
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			if(m_pColumns.Count != values.Length){
				throw new Exception("Each column must have corresponding value !");
			}

			bool unlock = true;
			// Table is already locked, don't lock it
			if(this.TableLocked){
				unlock = false;
			}
			else{
				LockTable(15);
			}

			// Construct record data
			byte[] record = LDB_Record.CreateRecord(this,values);

			// Get free data pages
			DataPage[] dataPages = GetDataPages(0,(int)Math.Ceiling(record.Length / (double)m_DataPageDataAreaSize));

			DbFile.StoreDataToDataPages(m_DataPageDataAreaSize,record,dataPages);

			if(unlock){
				UnlockTable();
			}
		}
	
		#endregion

		#region method DeleteRecord

		/// <summary>
		/// Deletes current record.
		/// </summary>
		public void DeleteCurrentRecord()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			if(m_pCurrentRecord == null){
				throw new Exception("There is no current record !");
			}

			bool unlock = true;
			// Table is already locked, don't lock it
			if(this.TableLocked){
				unlock = false;
			}
			else{
				LockTable(15);
			}
						
			// Release all data pages hold by this row
			DataPage[] dataPages = m_pCurrentRecord.DataPages;			
			for(int i=0;i<dataPages.Length;i++){
				DataPage p = dataPages[i];

				byte[] dataPage = DataPage.CreateDataPage(m_DataPageDataAreaSize,false,0,0,0,new byte[m_DataPageDataAreaSize]);
				SetFilePosition(p.Pointer);
				WriteToFile(dataPage,0,dataPage.Length);
			}
			
			// Increase free data pages count info in table header
			byte[] freeDataPagesCount = new byte[8];
			SetFilePosition(52);
			ReadFromFile(freeDataPagesCount,0,freeDataPagesCount.Length);
			freeDataPagesCount = ldb_Utils.LongToByte(ldb_Utils.ByteToLong(freeDataPagesCount,0) + dataPages.Length);
			SetFilePosition(52);
			WriteToFile(freeDataPagesCount,0,freeDataPagesCount.Length);

			if(unlock){
				UnlockTable();
			}

			// Activate next record **** Change it ???
			NextRecord();
		}

		#endregion


		#region static method StoreDataToDataPages

		/// <summary>
		/// Stores data to specified data pages.
		/// </summary>
		/// <param name="dataPageDataAreaSize">Data page data area size.</param>
		/// <param name="data">Data to store.</param>
		/// <param name="dataPages">Data pages where to store data.</param>
		internal static void StoreDataToDataPages(int dataPageDataAreaSize,byte[] data,DataPage[] dataPages)
		{				
			if((int)Math.Ceiling(data.Length / (double)dataPageDataAreaSize) > dataPages.Length){
				throw new Exception("There isn't enough data pages to store data ! Data needs '" + (int)Math.Ceiling(data.Length / (double)dataPageDataAreaSize) + "' , but available '" + dataPages.Length + "'.");
			}

			//--- Store data to data page(s) -----------------------//
			long position = 0;
			for(int i=0;i<dataPages.Length;i++){
				if((data.Length - position) > dataPageDataAreaSize){
					byte[] d = new byte[dataPageDataAreaSize];
					Array.Copy(data,position,d,0,d.Length);					
					dataPages[i].WriteData(d);
					position += dataPageDataAreaSize;					
				}
				else{
					byte[] d = new byte[data.Length - position];
					Array.Copy(data,position,d,0,d.Length);					
					dataPages[i].WriteData(d);
				}
			}
			//------------------------------------------------------//	
		}

		#endregion


		#region method GetDataPages

		/// <summary>
		/// Gets specified number of free data pages. If free data pages won't exist, creates new ones.
		/// Data pages are marked as used and OwnerDataPagePointer and NextDataPagePointer is set as needed.
		/// </summary>
		/// <param name="ownerDataPagePointer">Owner data page pointer that own first requested data page. If no owner then this value is 0.</param>
		/// <param name="count">Number of data pages wanted.</param>
		internal DataPage[] GetDataPages(long ownerDataPagePointer,int count)
		{
			if(!this.TableLocked){
				throw new Exception("Table must be locked to acess GetDataPages() method !");
			}

			ArrayList freeDataPages = new ArrayList();

			// Get free data pages count from table header
			byte[] freeDataPagesCount = new byte[8];
			SetFilePosition(52);
			ReadFromFile(freeDataPagesCount,0,freeDataPagesCount.Length);
			long nFreeDataPages = ldb_Utils.ByteToLong(freeDataPagesCount,0);

			// We have plenty free data pages and enough for count requested, find requested count free data pages
			if(nFreeDataPages > 1000 && nFreeDataPages > count){
				long nextDataPagePointer = m_DatapagesStartOffset + 1;
				while(freeDataPages.Count < count){
					DataPage dataPage = new DataPage(m_DataPageDataAreaSize,this,nextDataPagePointer);
					if(!dataPage.Used){
						dataPage.Used = true;
						freeDataPages.Add(dataPage);
					}

					nextDataPagePointer += m_DataPageDataAreaSize + 33;
				}

				// Decrease free data pages count in table header
				SetFilePosition(52);
				ReadFromFile(ldb_Utils.LongToByte(nFreeDataPages - count),0,8);
			}
			// Just create new data pages
			else{
				for(int i=0;i<count;i++){
					byte[] dataPage = DataPage.CreateDataPage(m_DataPageDataAreaSize,true,0,0,0,new byte[m_DataPageDataAreaSize]);
					GoToFileEnd();
					long dataPageStartPointer = GetFilePosition();
					WriteToFile(dataPage,0,dataPage.Length);

					freeDataPages.Add(new DataPage(m_DataPageDataAreaSize,this,dataPageStartPointer));
				}
			}
			
			// Relate data pages (chain)
			for(int i=0;i<freeDataPages.Count;i++){
				// First data page
				if(i == 0){
					// Owner data page poitner specified for first data page
					if(ownerDataPagePointer > 0){
						((DataPage)freeDataPages[i]).OwnerDataPagePointer = ownerDataPagePointer;
					}

					// There is continuing data page
					if(freeDataPages.Count > 1){
						((DataPage)freeDataPages[i]).NextDataPagePointer = ((DataPage)freeDataPages[i + 1]).Pointer;
					}
				}
				// Last data page
				else if(i == (freeDataPages.Count - 1)){
					((DataPage)freeDataPages[i]).OwnerDataPagePointer = ((DataPage)freeDataPages[i - 1]).Pointer;
				}
				// Middle range data page
				else{
					((DataPage)freeDataPages[i]).OwnerDataPagePointer = ((DataPage)freeDataPages[i - 1]).Pointer;
					((DataPage)freeDataPages[i]).NextDataPagePointer = ((DataPage)freeDataPages[i + 1]).Pointer;
				}
			}

			DataPage[] retVal = new DataPage[freeDataPages.Count];
			freeDataPages.CopyTo(retVal);

			return retVal;
		}

		#endregion

		#region method AddColumn

		/// <summary>
		/// Adds column to db file.
		/// </summary>
		/// <param name="column"></param>
		internal void AddColumn(LDB_DataColumn column)
		{
			// Find free column data area

			// Set position over version, free data pages count and data page data area size
			m_pDbFile.Position = 68;

			long freeColumnPosition = -1;
			StreamLineReader r = new StreamLineReader(m_pDbFile);
			// Loop all columns data areas, see it there any free left
			for(int i=0;i<100;i++){
				byte[] columnInfo = r.ReadLine();
				if(columnInfo == null){
					throw new Exception("Invalid columns data area length !");
				}

				// We found unused column data area
				if(columnInfo[0] == '\0'){
					freeColumnPosition = m_pDbFile.Position;
					break;
				}
			}
			m_FilePosition = m_pDbFile.Position;

			if(freeColumnPosition != -1){
				// TODO: If there is data ???

				// Move to row start
				SetFilePosition(GetFilePosition() - 102);

				// Store column
				byte[] columnData = column.ToColumnInfo();
				WriteToFile(columnData,0,columnData.Length);
			}
			else{
				throw new Exception("Couldn't find free column space ! ");
			}
		}

		#endregion

		#region method RemoveColumn

		/// <summary>
		/// Removes specified column from database file.
		/// </summary>
		/// <param name="column"></param>
		internal void RemoveColumn(LDB_DataColumn column)
		{
			throw new Exception("TODO:");
		}

		#endregion


		#region method ReadFromFile

		/// <summary>
		/// Reads data from file.
		/// </summary>
		/// <param name="data">Buffer where to store readed data..</param>
		/// <param name="offset">Offset in array to where to start storing readed data.</param>
		/// <param name="count">Number of bytes to read.</param>
		/// <returns></returns>
		internal int ReadFromFile(byte[] data,int offset,int count)
		{
			int readed = m_pDbFile.Read(data,offset,count);
			m_FilePosition += readed;

			return readed;
		}

		#endregion

		#region method WriteToFile

		/// <summary>
		/// Writes data to file.
		/// </summary>
		/// <param name="data">Data to write.</param>
		/// <param name="offset">Offset in array from where to start writing data.</param>
		/// <param name="count">Number of bytes to write.</param>
		/// <returns></returns>
		internal void WriteToFile(byte[] data,int offset,int count)
		{
			m_pDbFile.Write(data,offset,count);
			m_FilePosition += count;
		}

		#endregion

		#region method GetFilePosition

		/// <summary>
		/// Gets current position in file.
		/// </summary>
		/// <returns></returns>
		internal long GetFilePosition()
		{
			return m_FilePosition;
		}

		#endregion

		#region method SetFilePosition

		/// <summary>
		/// Sets file position.
		/// </summary>
		/// <param name="position">Position in file.</param>
		internal void SetFilePosition(long position)
		{
			if(m_FilePosition != position){
				m_pDbFile.Position = position;
				m_FilePosition = position;
			}
		}

		#endregion

		#region method GoToFileEnd

		/// <summary>
		/// Moves position to the end of file.
		/// </summary>
		private void GoToFileEnd()
		{
			m_pDbFile.Position = m_pDbFile.Length;
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_FileLength;
		}

		#endregion

		
		#region Properties Implementation

		/// <summary>
		/// Gets if there is active database file.
		/// </summary>
		public bool IsDatabaseOpen
		{
			get{ return m_pDbFile != null; }
		}

		/// <summary>
		/// Gets open database file name. Throws exception if database isn't open.
		/// </summary>
		public string FileName
		{
			get{
				if(!this.IsDatabaseOpen){
					throw new Exception("Database isn't open, please open database first !");
				}

				return m_DbFileName; 
			}
		}

		/// <summary>
		/// Gets table columns. Throws exception if database isn't open.
		/// </summary>
		public LDB_DataColumnCollection Columns
		{
			get{ 
				if(!this.IsDatabaseOpen){
					throw new Exception("Database isn't open, please open database first !");
				}

				return m_pColumns; 
			}
		}


		/// <summary>
		/// Gets current record. Returns null if there isn't current record.
		/// </summary>
		public LDB_Record CurrentRecord
		{
			get{ return m_pCurrentRecord; }
		}

		/// <summary>
		/// Gets table is locked.
		/// </summary>
		public bool TableLocked
		{
			get{ return m_TableLocked; }
		}

		/// <summary>
		/// Gets how much data data page can store.
		/// </summary>
		public int DataPageDataAreaSize
		{
			get{ return m_DataPageDataAreaSize; }
		}

		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions