context: //  
  //  ScatteringStoreDataAccessor.cs
  //  
  //  Author:
  //       Antonello Provenzano <antonello@deveel.com>
  //       Tobias Downer <toby@mckoi.com>
  // 
  //  Copyright (c) 2009 Deveel
  // 
  //  This program is free software: you can redistribute it and/or modify
  //  it under the terms of the GNU General Public License as published by
  //  the Free Software Foundation, either version 3 of the License, or
  //  (at your option) any later version.
  // 
  //  This program is distributed in the hope that it will be useful,
  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //  GNU General Public License for more details.
  // 
  //  You should have received a copy of the GNU General Public License
  //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
  using System;
  using System.Collections;
  using System.IO;
  using System.Text;
  
  namespace Deveel.Data.Store {
  	/// <summary>
  	/// An implementation of <see cref="IStoreDataAccessor"/> that scatters the 
  	/// addressible data resource across multiple files in the file system.
  	/// </summary>
  	/// <remarks>
  	/// When one store data resource reaches a certain threshold size, the content 
  	/// <i>flows</i> over to the next file.
  	/// </remarks>
  	public class ScatteringStoreDataAccessor : IStoreDataAccessor {
  		/// <summary>
  		///The path of this store in the file system. 
  		/// </summary>
  		private readonly string path;
  
  		/// <summary>
  		/// The name of the file in the file system minus the extension.
  		/// </summary>
  		private readonly String file_name;
  
  		/// <summary>
  		/// The extension of the first file in the sliced set.
  		/// </summary>
  		private readonly String first_ext;
  
  		/// <summary>
  		/// The maximum size a file slice can grow too before a new slice is created. 
  		/// </summary>
  		private readonly long max_slice_size;
  
  		/// <summary>
  		/// The list of RandomAccessFile objects for each file that represents a
  		/// slice of the store.  (FileSlice objects)
  		/// </summary>
  		private ArrayList slice_list;
  
  		/// <summary>
  		/// The current actual physical size of the store data on disk.
  		/// </summary>
  		private long true_file_length;
  
  		/// <summary>
  		/// A lock when modifying the true_data_size, and slice_list. 
  		/// </summary>
  		private readonly Object l = new Object();
  
  		/// <summary>
  		/// Set when the store is openned.
  		/// </summary>
  		private bool m_open = false;
  
  		/// <summary>
  		/// Constructs the store data accessor.
  		/// </summary>
  		/// <param name="path"></param>
  		/// <param name="file_name"></param>
  		/// <param name="first_ext"></param>
  		/// <param name="max_slice_size"></param>
  		public ScatteringStoreDataAccessor(string path, string file_name, string first_ext, long max_slice_size) {
  			slice_list = new ArrayList();
  			this.path = path;
  			this.file_name = file_name;
  			this.first_ext = first_ext;
  			this.max_slice_size = max_slice_size;
  		}
  
  		/// <summary>
  		/// Given a file, this will convert to a scattering file store with files 
  		/// no larger than the maximum slice size.
  		/// </summary>
  		/// <param name="f"></param>
  		public void ConvertToScatteringStore(string f) {
  			const int BUFFER_SIZE = 65536;
  
  			FileStream src = new FileStream(f, FileMode.OpenOrCreate, FileAccess.ReadWrite);
  			long file_size = new FileInfo(f).Length;
  			long current_p = max_slice_size;
  			long to_write = System.Math.Min(file_size - current_p, max_slice_size);
  			int write_to_part = 1;
  
  			byte[] copy_buffer = new byte[BUFFER_SIZE];
  
  			while (to_write > 0) {
  
  				src.Seek(current_p, SeekOrigin.Begin);
  
  				string to_f = SlicePartFile(write_to_part);
  				if (File.Exists(to_f)) {
  					throw new IOException("Copy error, slice already exists.");
  				}
  				FileStream to_raf = new FileStream(to_f, FileMode.OpenOrCreate, FileAccess.Write);
  
  				while (to_write > 0) {
  					int size_to_copy = (int)System.Math.Min(BUFFER_SIZE, to_write);
  
  					src.Read(copy_buffer, 0, size_to_copy);
  					to_raf.Write(copy_buffer, 0, size_to_copy);
  
  					current_p += size_to_copy;
  					to_write -= size_to_copy;
  				}
  
  				to_raf.Flush();
  				to_raf.Close();
  
  				to_write = System.Math.Min(file_size - current_p, max_slice_size);
  				++write_to_part;
  			}
  
  			// Truncate the source file
  			if (file_size > max_slice_size) {
  				src.Seek(0, SeekOrigin.Begin);
  				src.SetLength(max_slice_size);
  			}
  			src.Close();
  
  		}
  
  		/// <summary>
  		/// Given an index value, this will returns the path to the nth slice 
  		/// in the file system.
  		/// </summary>
  		/// <param name="i"></param>
  		/// <remarks>
  		/// For example, given '4' will return [file name].004, given 1004 will return 
  		/// [file name].1004, etc.
  		/// </remarks>
  		/// <returns></returns>
  		private string SlicePartFile(int i) {
  			if (i == 0) {
  				return Path.Combine(path, file_name + "." + first_ext);
  			}
  			StringBuilder fn = new StringBuilder();
  			fn.Append(file_name);
  			fn.Append(".");
  			if (i < 10) {
  				fn.Append("00");
  			} else if (i < 100) {
  				fn.Append("0");
  			}
  			fn.Append(i);
  			return Path.Combine(path, fn.ToString());
  		}
  
  		/// <summary>
  		/// Counts the number of files in the file store that represent this store.
  		/// </summary>
  		private int StoreFilesCount {
  			get {
  				int i = 0;
  				string f = SlicePartFile (i);
  				while (File.Exists (f)) {
  					++i;
  					f = SlicePartFile (i);
  				}
  				return i;
  			}
  		}
  
  		/// <summary>
  		/// Creates a <see cref="IStoreDataAccessor"/> object for accessing a given slice.
  		/// </summary>
  		/// <param name="file"></param>
  		/// <returns>
  		/// </returns>
  		private IStoreDataAccessor CreateSliceDataAccessor(string file) {
  			// Currently we only support an IOStoreDataAccessor object.
  			return new IOStoreDataAccessor(file);
  		}
  
  		/// <summary>
  		/// Discovers the size of the data resource (doesn't require the file 
  		/// to be open). 
  		/// </summary>
  		/// <returns></returns>
  		private long DiscoverSize() {
  			long running_total = 0;
  
  			lock (l) {
  				// Does the file exist?
  				int i = 0;
  				string f = SlicePartFile(i);
  				while (File.Exists(f)) {
  					running_total += CreateSliceDataAccessor(f).Size;
  
  					++i;
  					f = SlicePartFile(i);
  				}
  			}
  
  			return running_total;
  		}
  
  		// ---------- Implemented from IStoreDataAccessor ----------
  
  		public void Open(bool read_only) {
  			long running_length;
  
  			lock (l) {
  				slice_list = new ArrayList();
  
  				// Does the file exist?
  				string f = SlicePartFile(0);
  				bool open_existing = File.Exists(f);
  
  				// If the file already exceeds the threshold and there isn't a secondary
  				// file then we need to convert the file.
  
gt: 				if (open_existing && f.Length > max_slice_size) {