// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace Spludlow { public class HashStore { private string _StoreDirectory; private HashSet _HashSet; private HashMethod _HashMethod = Spludlow.Hashing.SHA1HexFile; private object _Lock = new object(); public delegate string HashMethod(string filename); public HashStore(string storeDirectory) { this._StoreDirectory = storeDirectory; this.Start(); } public HashStore(string storeDirectory, HashMethod hashMethod) { this._StoreDirectory = storeDirectory; this._HashMethod = hashMethod; this.Start(); } private void Start() { if (Directory.Exists(this._StoreDirectory) == false) Directory.CreateDirectory(this._StoreDirectory); this.Refresh(); } public int Length { get { lock (this._Lock) { return this._HashSet.Count; } } } public void Refresh() { lock (this._Lock) { this._HashSet = new HashSet(); foreach (string filename in Directory.GetFiles(this._StoreDirectory, "*", SearchOption.AllDirectories)) this._HashSet.Add(Path.GetFileName(filename)); } } public string Hash(string filename) { return this._HashMethod(filename); } public bool Add(string filename) { return this.Add(filename, false); } public bool Add(string filename, bool move) { return Add(filename, move, null); } public bool Add(string filename, bool move, string sha1) { // May already have if (sha1 == null) sha1 = this._HashMethod(filename); bool adding = false; lock (this._Lock) { if (this._HashSet.Contains(sha1) == false) { adding = true; this._HashSet.Add(sha1); } } if (adding == true) { string storeFilename = this.StoreFilename(sha1, true); if (move == false) File.Copy(filename, storeFilename); else File.Move(filename, storeFilename); } return adding; } public bool Delete(string sha1) { bool deleting = false; lock (this._Lock) { if (this._HashSet.Contains(sha1) == true) { deleting = true; this._HashSet.Remove(sha1); string storeFilename = this.StoreFilename(sha1, false); File.Delete(storeFilename); } } return deleting; } public bool Exists(string sha1) { lock (this._Lock) { return this._HashSet.Contains(sha1); } } public string Filename(string sha1) { if (this.Exists(sha1) == false) return null; return this.StoreFilename(sha1, false); } public string[] Hashes() { lock (this._Lock) { return this._HashSet.ToArray(); } } public string[] FileNames() { string[] hashes = this.Hashes(); string[] fileNames = new string[hashes.Length]; for (int index = 0; index < hashes.Length; ++index) fileNames[index] = this.StoreFilename(hashes[index], false); return fileNames; } private string StoreFilename(string sha1, bool writeMode) { //if (sha1.Length != 40) // throw new ApplicationException("Bad sha1: " + sha1); StringBuilder path = new StringBuilder(); path.Append(this._StoreDirectory); path.Append(@"\"); path.Append(sha1.Substring(0, 2)); path.Append(@"\"); path.Append(sha1); string filename = path.ToString(); if (writeMode == true) { string directory = Path.GetDirectoryName(filename); if (Directory.Exists(directory) == false) Directory.CreateDirectory(directory); } return filename; } } }