using System; using System.IO; using System.Text; using TweetDuck.Core.Utils; namespace TweetDuck.Data{ sealed class CombinedFileStream : IDisposable{ public const char KeySeparator = '|'; private readonly Stream stream; public CombinedFileStream(Stream stream){ this.stream = stream; } public void WriteFile(string[] identifier, string path){ WriteFile(string.Join(KeySeparator.ToString(), identifier), path); } public void WriteFile(string identifier, string path){ byte[] name = Encoding.UTF8.GetBytes(identifier); if (name.Length > 255){ throw new ArgumentOutOfRangeException("Identifier cannot be 256 or more characters long: "+identifier); } byte[] contents; using(FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ int index = 0; int left = (int)fileStream.Length; contents = new byte[left]; while(left > 0){ int read = fileStream.Read(contents, index, left); index += read; left -= read; } } stream.WriteByte((byte)name.Length); stream.Write(name, 0, name.Length); stream.Write(BitConverter.GetBytes(contents.Length), 0, 4); stream.Write(contents, 0, contents.Length); } public Entry ReadFile(){ int nameLength = stream.ReadByte(); if (nameLength == -1){ return null; } byte[] name = new byte[nameLength]; stream.Read(name, 0, nameLength); byte[] contentLength = new byte[4]; stream.Read(contentLength, 0, 4); byte[] contents = new byte[BitConverter.ToInt32(contentLength, 0)]; stream.Read(contents, 0, contents.Length); return new Entry(Encoding.UTF8.GetString(name), contents); } public string SkipFile(){ int nameLength = stream.ReadByte(); if (nameLength == -1){ return null; } byte[] name = new byte[nameLength]; stream.Read(name, 0, nameLength); byte[] contentLength = new byte[4]; stream.Read(contentLength, 0, 4); stream.Position += BitConverter.ToInt32(contentLength, 0); string keyName = Encoding.UTF8.GetString(name); return StringUtils.ExtractBefore(keyName, KeySeparator); } public void Flush(){ stream.Flush(); } void IDisposable.Dispose(){ stream.Dispose(); } public class Entry{ public string Identifier { get; } public string KeyName{ get{ return StringUtils.ExtractBefore(Identifier, KeySeparator); } } public string[] KeyValue{ get{ int index = Identifier.IndexOf(KeySeparator); return index == -1 ? new string[0] : Identifier.Substring(index+1).Split(KeySeparator); } } private readonly byte[] contents; public Entry(string identifier, byte[] contents){ this.Identifier = identifier; this.contents = contents; } public void WriteToFile(string path){ File.WriteAllBytes(path, contents); } public void WriteToFile(string path, bool createDirectory){ if (createDirectory){ string dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir)){ Directory.CreateDirectory(dir); } } File.WriteAllBytes(path, contents); } } } }