/ToMigrate/Raven.Database/FileSystem/Util/StorageStream.cs

https://github.com/fitzchak/ravendb · C# · 272 lines · 234 code · 38 blank · 0 comment · 35 complexity · 4c118e0ba5e5ca4ec6ffd64eea465c68 MD5 · raw file

  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Threading;
  5. using Raven.Abstractions.Exceptions;
  6. using Raven.Database.FileSystem.Storage;
  7. using Raven.Json.Linq;
  8. using Raven.Abstractions.FileSystem;
  9. namespace Raven.Database.FileSystem.Util
  10. {
  11. public class StorageStream : Stream
  12. {
  13. private readonly RavenFileSystem fileSystem;
  14. private readonly ITransactionalStorage storage;
  15. private const int PagesBatchSize = 64;
  16. private long currentOffset;
  17. private long currentPageFrameOffset;
  18. private bool disposed;
  19. private FileAndPagesInformation fileAndPages;
  20. private FileHeader fileHeader;
  21. protected byte[] InnerBuffer;
  22. protected int InnerBufferOffset = 0;
  23. private int writtingPagePosition;
  24. protected StorageStream(RavenFileSystem fileSystem, ITransactionalStorage storage, string fileName, RavenJObject metadata, StorageStreamAccess storageStreamAccess)
  25. {
  26. this.fileSystem = fileSystem;
  27. this.storage = storage;
  28. StorageStreamAccess = storageStreamAccess;
  29. Name = fileName;
  30. switch (storageStreamAccess)
  31. {
  32. case StorageStreamAccess.Read:
  33. storage.Batch(accessor => fileHeader = accessor.ReadFile(fileName));
  34. if (fileHeader.TotalSize == null)
  35. {
  36. throw new FileNotFoundException("File is not uploaded yet");
  37. }
  38. Metadata = fileHeader.Metadata;
  39. Seek(0, SeekOrigin.Begin);
  40. break;
  41. case StorageStreamAccess.CreateAndWrite:
  42. if (this.fileSystem == null)
  43. throw new ArgumentNullException("fileSystem");
  44. storage.Batch(accessor =>
  45. {
  46. using (fileSystem.DisableAllTriggersForCurrentThread())
  47. {
  48. fileSystem.Files.IndicateFileToDelete(fileName, null);
  49. }
  50. var putResult = accessor.PutFile(fileName, null, metadata);
  51. fileSystem.Search.Index(fileName, metadata, putResult.Etag);
  52. });
  53. Metadata = metadata;
  54. break;
  55. default:
  56. throw new ArgumentOutOfRangeException("storageStreamAccess", storageStreamAccess, "Unknown value");
  57. }
  58. }
  59. public StorageStreamAccess StorageStreamAccess { get; private set; }
  60. public string Name { get; private set; }
  61. public RavenJObject Metadata { get; private set; }
  62. private long CurrentPageFrameSize
  63. {
  64. get { return fileAndPages.Pages.Sum(item => item.Size); }
  65. }
  66. public override bool CanRead
  67. {
  68. get { return StorageStreamAccess == StorageStreamAccess.Read && fileHeader.TotalSize.HasValue; }
  69. }
  70. public override bool CanSeek
  71. {
  72. get { return StorageStreamAccess == StorageStreamAccess.Read && fileHeader.TotalSize.HasValue; }
  73. }
  74. public override bool CanWrite
  75. {
  76. get { return StorageStreamAccess == StorageStreamAccess.CreateAndWrite; }
  77. }
  78. public override long Length
  79. {
  80. get { return fileHeader.TotalSize ?? 0; }
  81. }
  82. public override long Position
  83. {
  84. get { return currentOffset; }
  85. set { Seek(value, SeekOrigin.Begin); }
  86. }
  87. public static StorageStream Reading(ITransactionalStorage storage, string fileName)
  88. {
  89. return new StorageStream(null, storage, fileName, null, StorageStreamAccess.Read);
  90. }
  91. public static StorageStream CreatingNewAndWritting(RavenFileSystem fileSystem, string fileName, RavenJObject metadata)
  92. {
  93. return new StorageStream(fileSystem, fileSystem.Storage, fileName, metadata, StorageStreamAccess.CreateAndWrite);
  94. }
  95. public override long Seek(long offset, SeekOrigin origin)
  96. {
  97. switch (origin)
  98. {
  99. case SeekOrigin.Begin:
  100. break;
  101. case SeekOrigin.Current:
  102. offset = currentOffset + offset;
  103. break;
  104. case SeekOrigin.End:
  105. offset = Length - offset - 1;
  106. break;
  107. default:
  108. throw new ArgumentOutOfRangeException("origin");
  109. }
  110. MovePageFrame(offset);
  111. return currentOffset;
  112. }
  113. private void MovePageFrame(long offset)
  114. {
  115. offset = Math.Min(Length, offset);
  116. if (offset < currentPageFrameOffset || fileAndPages == null)
  117. {
  118. storage.Batch(accessor => fileAndPages = accessor.GetFile(Name, 0, PagesBatchSize));
  119. currentPageFrameOffset = 0;
  120. }
  121. while (currentPageFrameOffset + CurrentPageFrameSize - 1 < offset)
  122. {
  123. var lastPageFrameSize = CurrentPageFrameSize;
  124. var nextPageIndex = fileAndPages.Start + fileAndPages.Pages.Count;
  125. storage.Batch(accessor => fileAndPages = accessor.GetFile(Name, nextPageIndex, PagesBatchSize));
  126. if (fileAndPages.Pages.Count < 1)
  127. {
  128. fileAndPages.Start = 0;
  129. break;
  130. }
  131. currentPageFrameOffset += lastPageFrameSize;
  132. }
  133. currentOffset = offset;
  134. }
  135. public override void SetLength(long value)
  136. {
  137. throw new NotSupportedException();
  138. }
  139. public override int Read(byte[] buffer, int offset, int count)
  140. {
  141. if (currentOffset >= Length)
  142. {
  143. return 0;
  144. }
  145. var innerBuffer = new byte[StorageConstants.MaxPageSize];
  146. var pageOffset = currentPageFrameOffset;
  147. var length = 0L;
  148. var startingOffset = currentOffset;
  149. foreach (var page in fileAndPages.Pages)
  150. {
  151. if (pageOffset <= currentOffset && currentOffset < pageOffset + page.Size)
  152. {
  153. var pageLength = 0;
  154. storage.Batch(accessor => pageLength = accessor.ReadPage(page.Id, innerBuffer));
  155. var sourceIndex = currentOffset - pageOffset;
  156. length = Math.Min(innerBuffer.Length - sourceIndex,
  157. Math.Min(pageLength, Math.Min(buffer.Length - offset, Math.Min(pageLength - sourceIndex, count))));
  158. Array.Copy(innerBuffer, sourceIndex, buffer, offset, length);
  159. break;
  160. }
  161. pageOffset += page.Size;
  162. }
  163. MovePageFrame(currentOffset + length);
  164. return Convert.ToInt32(currentOffset - startingOffset);
  165. }
  166. public override void Flush()
  167. {
  168. if (InnerBuffer != null && InnerBufferOffset > 0)
  169. {
  170. int retries = 50;
  171. bool shouldRetry;
  172. do
  173. {
  174. try
  175. {
  176. storage.Batch(accessor =>
  177. {
  178. var hashKey = accessor.InsertPage(InnerBuffer, InnerBufferOffset);
  179. accessor.AssociatePage(Name, hashKey, writtingPagePosition, InnerBufferOffset);
  180. fileSystem.PutTriggers.Apply(trigger => trigger.OnUpload(Name, Metadata, hashKey, writtingPagePosition, InnerBufferOffset));
  181. });
  182. writtingPagePosition++;
  183. shouldRetry = false;
  184. }
  185. catch (ConcurrencyException)
  186. {
  187. if (retries-- > 0)
  188. {
  189. shouldRetry = true;
  190. Thread.Sleep(50);
  191. continue;
  192. }
  193. throw;
  194. }
  195. } while (shouldRetry);
  196. InnerBuffer = null;
  197. InnerBufferOffset = 0;
  198. }
  199. }
  200. public override void Write(byte[] buffer, int offset, int count)
  201. {
  202. var innerOffset = 0;
  203. while (innerOffset < count)
  204. {
  205. if (InnerBuffer == null)
  206. InnerBuffer = new byte[StorageConstants.MaxPageSize];
  207. var toCopy = Math.Min(StorageConstants.MaxPageSize - InnerBufferOffset, count - innerOffset);
  208. if (toCopy == 0)
  209. throw new Exception("Impossible");
  210. Array.Copy(buffer, offset + innerOffset, InnerBuffer, InnerBufferOffset, toCopy);
  211. InnerBufferOffset += toCopy;
  212. if (InnerBufferOffset == StorageConstants.MaxPageSize)
  213. Flush();
  214. innerOffset += toCopy;
  215. }
  216. }
  217. protected override void Dispose(bool disposing)
  218. {
  219. if (!disposed)
  220. {
  221. if (disposing)
  222. {
  223. Flush();
  224. if (StorageStreamAccess == StorageStreamAccess.CreateAndWrite)
  225. {
  226. storage.Batch(accessor => accessor.CompleteFileUpload(Name));
  227. fileSystem.PutTriggers.Apply(trigger => trigger.AfterUpload(Name, Metadata));
  228. }
  229. }
  230. disposed = true;
  231. }
  232. }
  233. }
  234. }