public void AppendNonAtomic(IEnumerable <TapeRecord> records) { if (records == null) { throw new ArgumentNullException("records"); } // if enumeration is lazy, then this would cause another enum //if (!records.Any()) // return; using (var file = OpenForWrite()) { file.Seek(0, SeekOrigin.End); foreach (var record in records) { if (record.Data.Length == 0) { throw new ArgumentException("Record must contain at least one byte."); } var versionToWrite = record.Version; TapeStreamSerializer.WriteRecord(file, record.Data, versionToWrite); } } }
public long TryAppend(byte[] buffer, TapeAppendCondition condition) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (buffer.Length == 0) { throw new ArgumentException("Buffer must contain at least one byte."); } using (var file = OpenForWrite()) { // we need to know version first. file.Seek(0, SeekOrigin.End); var version = TapeStreamSerializer.ReadVersionFromTheEnd(file); if (!condition.Satisfy(version)) { return(0); } var versionToWrite = version + 1; TapeStreamSerializer.WriteRecord(file, buffer, versionToWrite); return(versionToWrite); } }
public IEnumerable <TapeRecord> ReadRecords(long afterVersion, int maxCount) { if (afterVersion < 0) { throw new ArgumentOutOfRangeException("afterVersion", "Must be zero or greater."); } if (maxCount <= 0) { throw new ArgumentOutOfRangeException("maxCount", "Must be more than zero."); } // afterVersion + maxCount > long.MaxValue, but transformed to avoid overflow if (afterVersion > long.MaxValue - maxCount) { throw new ArgumentOutOfRangeException("maxCount", "Version will exceed long.MaxValue."); } var blob = _container.GetBlockBlobReference(_blobName); var dataExists = blob.Exists(); // we return empty result if writer didn't even start writing to the storage. if (!dataExists) { return(Enumerable.Empty <TapeRecord>()); } var blockList = blob.DownloadBlockList(); var range = GetReadRange(blockList, afterVersion, maxCount); if (range.Item4 == 0) { return(new TapeRecord[0]); } using (var s = blob.OpenRead()) { s.ReadAheadSize = range.Item2; s.Seek(range.Item1, SeekOrigin.Begin); using (var reader = new BinaryReader(s)) { var bytes = reader.ReadBytes(range.Item2); using (var ms = new MemoryStream(bytes)) { TapeStreamSerializer.SkipRecords(range.Item3, ms); var records = Enumerable.Range(0, range.Item4) .Select(i => TapeStreamSerializer.ReadRecord(ms)) .ToArray(); return(records); } } } }
public IEnumerable <TapeRecord> ReadRecords(long afterVersion, int maxCount) { if (afterVersion < 0) { throw new ArgumentOutOfRangeException("afterVersion", "Must be zero or greater."); } if (maxCount <= 0) { throw new ArgumentOutOfRangeException("maxCount", "Must be more than zero."); } // afterVersion + maxCount > long.MaxValue, but transformed to avoid overflow if (afterVersion > long.MaxValue - maxCount) { throw new ArgumentOutOfRangeException("maxCount", "Version will exceed long.MaxValue."); } if (!_data.Exists) { // file could've been created since the last check _data.Refresh(); if (!_data.Exists) { yield break; } } using (var file = OpenForRead()) { if (!TapeStreamSerializer.SkipRecords(afterVersion, file)) { yield break; } for (var i = 0; i < maxCount; i++) { if (file.Position == file.Length) { yield break; } var record = TapeStreamSerializer.ReadRecord(file); yield return(record); } } }
public long GetCurrentVersion() { try { using (var s = OpenForRead()) { // seek end s.Seek(0, SeekOrigin.End); return(TapeStreamSerializer.ReadVersionFromTheEnd(s)); } } catch (FileNotFoundException) { return(0); } catch (DirectoryNotFoundException) { return(0); } }
public long TryAppend(byte[] buffer, TapeAppendCondition appendCondition = new TapeAppendCondition()) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (buffer.Length == 0) { throw new ArgumentException("Buffer must contain at least one byte."); } long version; int lastBlockSize; long offset; long firstVersion; long count; List <string> blockNames; var blob = _container.GetBlockBlobReference(_blobName); if (blob.Exists()) { var blockList = blob.DownloadBlockList().ToArray(); blockNames = blockList.Select(bl => bl.Name).ToList(); var lastBlock = blockList.LastOrDefault(); if (default(ListBlockItem) == lastBlock) { version = 0; lastBlockSize = int.MaxValue; offset = 0; firstVersion = 1; count = 0; } else { var nameInfo = Naming.GetInfo(DecodeName(lastBlock.Name)); firstVersion = nameInfo.FirstVersion; version = nameInfo.FirstVersion - 1 + nameInfo.Count; count = nameInfo.Count; if (lastBlock.Size > int.MaxValue) { throw new InvalidOperationException("last block size must be in 'int' range"); } lastBlockSize = (int)lastBlock.Size; offset = blockList.Reverse().Skip(1).Sum(l => l.Size); } } else { version = 0; lastBlockSize = int.MaxValue; offset = 0; firstVersion = 1; count = 0; blockNames = new List <string>(); } if (!appendCondition.Satisfy(version)) { return(0); } if (version > long.MaxValue - 1) { throw new IndexOutOfRangeException("Version is more than long.MaxValue."); } if (buffer.Length > FourMb) { throw new ArgumentException("buffer size must be less than or equal to 4 Mb", "buffer"); } using (var outStream = new MemoryStream()) { if (buffer.Length < MaxBlockSize && lastBlockSize <= MaxBlockSize - buffer.Length) { // read old block using (var s = blob.OpenRead()) { s.Seek(offset, SeekOrigin.Begin); s.CopyTo(outStream); TapeStreamSerializer.WriteRecord(outStream, buffer, version + 1); count++; blockNames.RemoveAt(blockNames.Count - 1); } } else { TapeStreamSerializer.WriteRecord(outStream, buffer, version + 1); firstVersion = version + 1; count = 1; } var blockId = EncodeName(Naming.GetName(firstVersion, count)); string md5Hash; outStream.Seek(0, SeekOrigin.Begin); using (var md5 = MD5.Create()) { md5Hash = Convert.ToBase64String(md5.ComputeHash(outStream)); } outStream.Seek(0, SeekOrigin.Begin); blob.PutBlock(blockId, outStream, md5Hash); blockNames.Add(blockId); blob.PutBlockList(blockNames); return(version + 1); } }