⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cache.cs

📁 C#数据库中间层处理,支持主流数据库,如sql、oracle等
💻 CS
字号:
/*
 * Cache.cs
 *
 * Copyright (c) 2001, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This package is based on HypersonicSQL, originally developed by Thomas Mueller.
 *
 * C# port by Mark Tutt
 *
 */
namespace SharpHSQL
{
	using System;
	using System.Collections;
	using System.IO;

	/**
	 * Cache class declaration
	 * <P>The cache class implements the handling of cached tables.
	 *
	 * @version 1.0.0.1
	 * @see Row
	 * @see CacheFree
	 */
	class Cache 
	{
		private FileStream rFile;
		private static int LENGTH = 1 << 14;
		private static int MAX_CACHE_SIZE = LENGTH * 3 / 4;
		private Row[]      rData;
		private Row[]	   rWriter;
		private Row	       rFirst;		   // must point to one of rData[]
		private Row		   rLastChecked;	   // can be any row
		private string	   sName;
		private static int MASK = (LENGTH) - 1;
		private int		   iFreePos;
		private static int FREE_POS_POS = 0;    // where iFreePos is saved
		private static int INITIAL_FREE_POS = 4;
		private static int MAX_FREE_COUNT = 1024;
		private CacheFree  fRoot;
		private int	       iFreeCount;
		private int	       iCacheSize;

		/**
		 * Cache constructor declaration
		 * <P>The cache constructor sets up the initial parameters of the cache
		 * object, setting the name used for the file, etc.
		 *
		 * @param name of database file
		 */
		public Cache(string name) 
		{
			sName = name;
			rData = new Row[LENGTH];
			rWriter = new Row[LENGTH];
		}

		/**
		 * open method declaration
		 * <P>The open method creates or opens a database file.
		 *
		 * @param flag that indicates if this cache is readonly
		 *
		 * @throws Exception
		 */
		public void open(bool ReadOnly) 
		{
			try 
			{
				bool exists = false;
				File    f = new File(sName);

				if (f.Exists && f.Length > FREE_POS_POS) 
				{
					exists = true;
				}

				if (ReadOnly)
				{
					rFile = new FileStream(sName, System.IO.FileMode.OpenOrCreate,FileAccess.Read);	
				}
				else
				{
					rFile = new FileStream(sName, System.IO.FileMode.OpenOrCreate,FileAccess.ReadWrite);	
				}
				
				if (exists) 
				{
					rFile.Seek(FREE_POS_POS,SeekOrigin.Begin);

					BinaryReader b = new BinaryReader(rFile);

					iFreePos = b.ReadInt32();
				} 
				else 
				{
					iFreePos = INITIAL_FREE_POS;
				}
			} 
			catch (Exception e) 
			{
				throw Trace.error(Trace.FILE_IO_ERROR,
					"error " + e + " opening " + sName);
			}
		}

		/**
		 * flush method declaration
		 * <P>The flush method saves all cached data to the file, saves the free position
		 * and closes the file.
		 *
		 * @throws Exception
		 */
		public void flush() 
		{
			try 
			{
				rFile.Seek(FREE_POS_POS,SeekOrigin.Begin);
				BinaryWriter b = new BinaryWriter(rFile);
				b.Write(iFreePos);
				saveAll();
				rFile.Close();
			} 
			catch (Exception e) 
			{
				throw Trace.error(Trace.FILE_IO_ERROR,
					"error " + e + " closing " + sName);
			}
		}

		/**
		 * shutdown method declaration
		 * <P>the shutdown method closes the cache file.  It does not flush pending writes.
		 *
		 * @throws Exception
		 */
		public void shutdown() 
		{
			try 
			{
				rFile.Close();
			} 
			catch (Exception e) 
			{
				throw Trace.error(Trace.FILE_IO_ERROR,
					"error " + e + " in shutdown " + sName);
			}
		}

		/**
		 * free method declaration
		 * <P>This method marks space in the database file as free.
		 * <P><B>Note: </B>If more than MAX_FREE_COUNT free positios then
		 * they are probably all are too small anyway; so we start a new list
		 * <P>todo: This is wrong when deleting lots of records
		 *
		 * @param r (Row object to be marked free)
		 * @param pos (Offset in the file this Row was stored at)
		 * @param length (Size of the Row object to free)
		 *
		 * @throws Exception
		 */
		public void free(Row r, int pos, int length) 
		{
			iFreeCount++;

			CacheFree n = new CacheFree();

			n.iPos = pos;
			n.iLength = length;

			if (iFreeCount > MAX_FREE_COUNT) 
			{
				iFreeCount = 0;
			} 
			else 
			{
				n.fNext = fRoot;
			}

			fRoot = n;

			// it's possible to remove roots to
			remove(r);
		}

		/**
		 * add method declaration
		 * <P>This method adds a Row to the Cache.  It walks the
		 * list of CacheFree objects to see if there is available space
		 * to store the new Row, reusing space if it exists, otherwise
		 * we grow the file.
		 *
		 * @param r (Row to be added to Cache)
		 *
		 * @throws Exception
		 */
		public void add(Row r) 
		{
			int       size = r.iSize;
			CacheFree f = fRoot;
			CacheFree last = null;
			int       i = iFreePos;

			while (f != null) 
			{
				if (Trace.TRACE) 
				{
					Trace.stop();
				}
				// first that is long enough
				if (f.iLength >= size) 
				{
					i = f.iPos;
					size = f.iLength - size;

					if (size < 8) 
					{

						// remove almost empty blocks
						if (last == null) 
						{
							fRoot = f.fNext;
						} 
						else 
						{
							last.fNext = f.fNext;
						}

						iFreeCount--;
					} 
					else 
					{
						f.iLength = size;
						f.iPos += r.iSize;
					}

					break;
				}

				last = f;
				f = f.fNext;
			}

			r.iPos = i;

			if (i == iFreePos) 
			{
				iFreePos += size;
			}

			int k = i & MASK;
			Row before = rData[k];

			if (before == null) 
			{
				before = rFirst;
			}

			r.insert(before);

			iCacheSize++;
			rData[k] = r;
			rFirst = r;
		}

		/**
		 * getRow method declaration
		 * <P>This method reads a Row object from the cache.
		 *
		 * @param pos (offset of the requested Row in the cache)
		 * @param t (Table this Row belongs to)
		 *
		 * @return The Row object as read from the cache.
		 *
		 * @throws Exception
		 */
		public Row getRow(int pos, Table t) 
		{
			int k = pos & MASK;
			Row r = rData[k];
			Row start = r;

			while (r != null) 
			{
				if (Trace.STOP) 
				{
					Trace.stop();
				}

				int p = r.iPos;

				if (p == pos) 
				{
					return r;
				} 
				else if ((p & MASK) != k) 
				{
					break;
				}

				r = r.rNext;

				if (r == start) 
				{
					break;
				}
			}

			Row before = rData[k];

			if (before == null) 
			{
				before = rFirst;
			}

			try 
			{
				rFile.Seek(pos,SeekOrigin.Begin);

				BinaryReader b = new BinaryReader(rFile);

				int  size = b.ReadInt32();
				byte[] buffer = new byte[size];

				buffer = b.ReadBytes(size);

				MemoryStream bin = new MemoryStream(buffer);
				BinaryReader din = new BinaryReader(bin);

				r = new Row(t, din, pos, before);
				r.iSize = size;
			} 
			catch (IOException e) 
			{
				Console.WriteLine(e.StackTrace);

				throw Trace.error(Trace.FILE_IO_ERROR, "reading: " + e);
			}

			// todo: copy & paste here
			iCacheSize++;
			rData[k] = r;
			rFirst = r;

			return r;
		}

		/**
		 * cleanUp method declaration
		 * <P>This method cleans up the cache when it grows too large. It works by
		 * checking Rows in held in the Cache's iLastAccess member and removing
		 * Rows that haven't been accessed in the longest time.
		 *
		 * @throws Exception
		 */
		public void cleanUp() 
		{
			if (iCacheSize < MAX_CACHE_SIZE) 
			{
				return;
			}

			int count = 0, j = 0;

			while (j++ < LENGTH && iCacheSize + LENGTH > MAX_CACHE_SIZE
				&& (count * 16) < LENGTH) 
			{
				if (Trace.STOP) 
				{
					Trace.stop();
				}

				Row r = getWorst();

				if (r == null) 
				{
					return;
				}

				if (r.bChanged) 
				{
					rWriter[count++] = r;
				} 
				else 
				{

					// here we can't remove roots
					if (!r.canRemove()) 
					{
						remove(r);
					}
				}
			}

			if (count != 0) 
			{
				saveSorted(count);
			}

			for (int i = 0; i < count; i++) 
			{

				// here we can't remove roots
				Row r = rWriter[i];

				if (!r.canRemove()) 
				{
					remove(r);
				}

				rWriter[i] = null;
			}
		}

		/**
		 * remove method declaration
		 * <P>This method is used to remove Rows from the Cache. It is called
		 * by the cleanUp method.
		 *
		 * @param r (Row to be removed)
		 *
		 * @throws Exception
		 */
		private void remove(Row r) 
		{
			if (Trace.ASSERT) 
			{
				Trace.assert(!r.bChanged);

				// make sure rLastChecked does not point to r
			}

			if (r == rLastChecked) 
			{
				rLastChecked = rLastChecked.rNext;

				if (rLastChecked == r) 
				{
					rLastChecked = null;
				}
			}

			// make sure rData[k] does not point here
			int k = r.iPos & MASK;

			if (rData[k] == r) 
			{
				Row n = r.rNext;

				rFirst = n;

				if (n == r || (n.iPos & MASK) != k) 
				{
					n = null;
				}

				rData[k] = n;
			}

			// make sure rFirst does not point here
			if (r == rFirst) 
			{
				rFirst = rFirst.rNext;

				if (r == rFirst) 
				{
					rFirst = null;
				}
			}

			r.free();

			iCacheSize--;
		}

		/**
		 * getWorst method declaration
		 * <P>This method finds the Row with the smallest (oldest) iLastAccess member.
		 * Called by the cleanup method.
		 *
		 * @return The selected Row object
		 *
		 * @throws Exception
		 */
		private Row getWorst() 
		{
			if (rLastChecked == null) 
			{
				rLastChecked = rFirst;
			}

			Row r = rLastChecked;

			if (r == null) 
			{
				return null;
			}

			Row candidate = r;
			int worst = Row.iCurrentAccess;

			// algorithm: check the next rows and take the worst
			for (int i = 0; i < 6; i++) 
			{
				int w = r.iLastAccess;

				if (w < worst) 
				{
					candidate = r;
					worst = w;
				}

				r = r.rNext;
			}

			rLastChecked = r.rNext;

			return candidate;
		}

		/**
		 * Method declaration
		 *
		 *
		 * @throws Exception
		 */
		private void saveAll() 
		{
			if (rFirst == null) 
			{
				return;
			}

			Row r = rFirst;

			while (true) 
			{
				int count = 0;
				Row begin = r;

				do 
				{
					if (Trace.STOP) 
					{
						Trace.stop();
					}

					if (r.bChanged) 
					{
						rWriter[count++] = r;
					}

					r = r.rNext;
				} while (r != begin && count < LENGTH);

				if (count == 0) 
				{
					return;
				}

				saveSorted(count);

				for (int i = 0; i < count; i++) 
				{
					rWriter[i] = null;
				}
			}
		}

		/**
		 * Method declaration
		 *
		 *
		 * @param count
		 *
		 * @throws Exception
		 */
		private void saveSorted(int count) 
		{
			if (count < 1)
			{
				return;
			}

			sort(rWriter, 0, count - 1);

			try 
			{
				rFile.Seek(rWriter[0].iPos,SeekOrigin.Begin);
				BinaryWriter b = new BinaryWriter(rFile);

				for (int i = 0; i < count; i++) 
				{
					b.Write(rWriter[i].write());
				}
			} 
			catch (Exception e) 
			{
				throw Trace.error(Trace.FILE_IO_ERROR, "saveSorted " + e);
			}
		}

		/**
		 * Method declaration
		 *
		 *
		 * @param w
		 * @param l
		 * @param r
		 *
		 * @throws Exception
		 */
		private static void sort(Row[] w, int l,
			int r) 
		{
			int i, j, p;

			while (r - l > 10) 
			{
				i = (r + l) >> 1;

				if (w[l].iPos > w[r].iPos) 
				{
					swap(w, l, r);
				}

				if (w[i].iPos < w[l].iPos) 
				{
					swap(w, l, i);
				} 
				else if (w[i].iPos > w[r].iPos) 
				{
					swap(w, i, r);
				}

				j = r - 1;

				swap(w, i, j);

				p = w[j].iPos;
				i = l;

				while (true) 
				{
					if (Trace.STOP) 
					{
						Trace.stop();
					}

					while (w[++i].iPos < p);

					while (w[--j].iPos > p);

					if (i >= j) 
					{
						break;
					}

					swap(w, i, j);
				}

				swap(w, i, r - 1);
				sort(w, l, i - 1);

				l = i + 1;
			}

			for (i = l + 1; i <= r; i++) 
			{
				if (Trace.STOP) 
				{
					Trace.stop();
				}

				Row t = w[i];

				for (j = i - 1; j >= l && w[j].iPos > t.iPos; j--) 
				{
					w[j + 1] = w[j];
				}

				w[j + 1] = t;
			}
		}

		/**
		 * Method declaration
		 *
		 *
		 * @param w
		 * @param a
		 * @param b
		 */
		private static void swap(Row[] w, int a, int b) 
		{
			Row t = w[a];

			w[a] = w[b];
			w[b] = t;
		}

	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -