using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using BizHawk.Common; namespace BizHawk.Client.Common { public static class StringLogUtil { public static bool DefaultToDisk; public static bool DefaultToAWE; public static IStringLog MakeStringLog() { if (DefaultToDisk) return new StreamStringLog(true); else if(DefaultToAWE) return new StreamStringLog(false); else return new ListStringLog(); } } public interface IStringLog : IDisposable, IEnumerable { void RemoveAt(int index); int Count { get; } void Clear(); void Add(string str); string this[int index] { get; set; } void Insert(int index, string val); void InsertRange(int index, IEnumerable collection); void AddRange(IEnumerable collection); void RemoveRange(int index, int count); IStringLog Clone(); void CopyTo(string[] array); void CopyTo(int index, string[] array, int arrayIndex, int count); } class ListStringLog : List, IStringLog { public IStringLog Clone() { ListStringLog ret = new ListStringLog(); ret.AddRange(this); return ret; } public void Dispose() { } } /// /// A dumb-ish IStringLog with storage on disk with no provision for recovering lost space, except upon Clear() /// The purpose here is to avoid having too complicated buggy logic or a dependency on sqlite or such. /// It should be faster than those alternatives, but wasteful of disk space. /// It should also be easier to add new IList-like methods than dealing with a database /// class StreamStringLog : IStringLog { List Offsets = new List(); long cursor = 0; BinaryWriter bw; BinaryReader br; bool mDisk; Stream stream; public StreamStringLog(bool disk) { mDisk = disk; if (disk) { var path = TempFileCleaner.GetTempFilename("movieOnDisk"); stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose); } else { stream = new AWEMemoryStream(); } bw = new BinaryWriter(stream); br = new BinaryReader(stream); } public IStringLog Clone() { StreamStringLog ret = new StreamStringLog(mDisk); //doesnt necessarily make sense to copy the mDisk value, they could be designated for different targets... for (int i = 0; i < Count; i++) ret.Add(this[i]); return ret; } public void Dispose() { stream.Dispose(); } public int Count { get { return Offsets.Count; } } public void Clear() { stream.SetLength(0); Offsets.Clear(); cursor = 0; } public void Add(string str) { Offsets.Add(stream.Position); bw.Write(str); bw.Flush(); } public void RemoveAt(int index) { Offsets.RemoveAt(index); //no garbage collection in the disk file... oh well. } public string this[int index] { get { stream.Position = Offsets[index]; return br.ReadString(); } set { stream.Position = stream.Length; Offsets[index] = stream.Position; bw.Write(value); bw.Flush(); } } public void Insert(int index, string val) { Offsets.Insert(index, stream.Position); bw.Write(val); bw.Flush(); } public void InsertRange(int index, IEnumerable collection) { foreach(var item in collection) Insert(index++,item); } public void AddRange(IEnumerable collection) { foreach (var item in collection) Add(item); } class Enumerator : IEnumerator { public StreamStringLog log; int index = -1; public string Current { get { return log[index]; } } object System.Collections.IEnumerator.Current { get { return log[index]; } } bool System.Collections.IEnumerator.MoveNext() { index++; if (index >= log.Count) { index = log.Count; return false; } return true; } void System.Collections.IEnumerator.Reset() { index = -1; } public void Dispose() { } } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator() { log = this }; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator() { log = this }; } public void RemoveRange(int index, int count) { int end = index + count - 1; for (int i = 0; i < count; i++) { RemoveAt(end); end--; } } public void CopyTo(string[] array) { for (int i = 0; i < Count; i++) array[i] = this[i]; } public void CopyTo(int index, string[] array, int arrayIndex, int count) { for (int i = 0; i < count; i++) array[i + arrayIndex] = this[index + i]; } } }