PageRenderTime 84ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Samples.BizTalk.Adapter.Tcp/Microsoft.Samples.BizTalk.Adapter.Tcp/SeekableReadOnlyStream.cs

#
C# | 365 lines | 160 code | 51 blank | 154 comment | 30 complexity | 8f984205f63ebf207c897a5b0dc106dc MD5 | raw file
  1. //---------------------------------------------------------------------
  2. // File: SeekableReadOnlyStream.cs
  3. //
  4. // Summary: A sample pipeline component which demonstrates how to promote message context
  5. // properties and write distinguished fields for XML messages using arbitrary
  6. // XPath expressions.
  7. //
  8. // Sample: Arbitrary XPath Property Handler Pipeline Component SDK
  9. //
  10. //---------------------------------------------------------------------
  11. // This file is part of the Microsoft BizTalk Server 2006 SDK
  12. //
  13. // Copyright (c) Microsoft Corporation. All rights reserved.
  14. //
  15. // This source code is intended only as a supplement to Microsoft BizTalk
  16. // Server 2006 release and/or on-line documentation. See these other
  17. // materials for detailed information regarding Microsoft code samples.
  18. //
  19. // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  20. // KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  21. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  22. // PURPOSE.
  23. //---------------------------------------------------------------------
  24. using System;
  25. using System.IO;
  26. using System.Diagnostics;
  27. namespace Microsoft.Samples.BizTalk.Adapter.Tcp
  28. {
  29. /// <summary>
  30. /// Implements a seekable read-only stream which uses buffering if
  31. /// underlying stream is not seekable. Buffer in memory has size
  32. /// threshold and overflows to disk (temporary file) if number of bytes.
  33. /// </summary>
  34. public class SeekableReadOnlyStream : Stream
  35. {
  36. /// <summary>
  37. /// Initializes a SeekableReadOnlyStream instance with base stream and
  38. /// buffering stream.
  39. /// </summary>
  40. /// <param name="baseStream">Base stream</param>
  41. /// <param name="overflowStream">Buffering stream</param>
  42. public SeekableReadOnlyStream(Stream baseStream, Stream bufferingStream)
  43. {
  44. if (null == baseStream)
  45. throw new ArgumentNullException("baseStream");
  46. if (null == bufferingStream)
  47. throw new ArgumentNullException("bufferingStream");
  48. // Sanity check - make sure that buffering stream is seekable
  49. if (!bufferingStream.CanSeek)
  50. throw new NotSupportedException("Buffering stream must be seekable");
  51. this.baseStream = baseStream;
  52. this.bufferingStream = bufferingStream;
  53. }
  54. /// <summary>
  55. /// Initializes a SeekableReadOnlyStream instance with base stream and inherently uses
  56. /// VirtualStream instance as buffering stream.
  57. /// </summary>
  58. /// <param name="baseStream">Base stream</param>
  59. public SeekableReadOnlyStream(Stream baseStream) : this(baseStream, new VirtualStream())
  60. {
  61. // Empty
  62. }
  63. /// <summary>
  64. /// Initializes a SeekableReadOnlyStream instance with base stream and buffer size, and
  65. /// inherently uses VirtualStream instance as buffering stream.
  66. /// </summary>
  67. /// <param name="baseStream">Base stream</param>
  68. /// <param name="bufferSize">Buffer size</param>
  69. public SeekableReadOnlyStream(Stream baseStream, int bufferSize) : this(baseStream, new VirtualStream(bufferSize))
  70. {
  71. // Empty
  72. }
  73. /// <summary>
  74. /// Gets a flag indicating whether this stream can be read.
  75. /// </summary>
  76. public override bool CanRead
  77. {
  78. get { return true; }
  79. }
  80. /// <summary>
  81. /// Gets a flag indicating whether this stream can be written to.
  82. /// </summary>
  83. public override bool CanWrite
  84. {
  85. get { return false; }
  86. }
  87. public override bool CanSeek
  88. {
  89. get { return true; }
  90. }
  91. /// <summary>
  92. /// Gets or sets a stream position.
  93. /// </summary>
  94. public override long Position
  95. {
  96. get
  97. {
  98. // Check if base stream is seekable
  99. if (baseStream.CanSeek)
  100. return baseStream.Position;
  101. return bufferingStream.Position;
  102. }
  103. set
  104. {
  105. // Check if base stream is seekable
  106. if (baseStream.CanSeek)
  107. {
  108. baseStream.Position = value;
  109. return;
  110. }
  111. // Check if current position is the same as being set
  112. if (bufferingStream.Position == value)
  113. return;
  114. // Check if stream position is being set to the value which is in already
  115. // read to the buffering stream space, i.e. less than current buffering stream
  116. // position or less than length of the buffering stream
  117. if (value < bufferingStream.Position || value < bufferingStream.Length)
  118. {
  119. // Just change position in the buffering stream
  120. bufferingStream.Position = value;
  121. }
  122. else
  123. {
  124. //
  125. // Need to read buffer from the base stream from the current position in
  126. // base stream to the position being set and write that buffer to the end
  127. // of the buffering stream
  128. //
  129. // Set position to the last byte in the buffering stream
  130. bufferingStream.Seek(0, SeekOrigin.End);
  131. // Read buffer from the base stream and write it to the buffering stream
  132. // in 4K chunks
  133. byte [] buffer = new byte[ 4096 ];
  134. long bytesToRead = value - bufferingStream.Position;
  135. while (bytesToRead > 0)
  136. {
  137. // Read to buffer 4K or byteToRead, whichever is less
  138. int bytesRead = baseStream.Read(buffer, 0, (int) Math.Min(bytesToRead, buffer.Length));
  139. // Check if any bytes were read
  140. if (0 == bytesRead)
  141. break;
  142. // Write read bytes to the buffering stream
  143. bufferingStream.Write(buffer, 0, bytesRead);
  144. // Decrease bytes to read counter
  145. bytesToRead -= bytesRead;
  146. }
  147. //
  148. // Since this stream is not writable, any attempt to point Position beyond the length
  149. // of the base stream will not succeed, and buffering stream position will be set to the
  150. // last byte in the buffering stream.
  151. //
  152. }
  153. }
  154. }
  155. /// <summary>
  156. /// Seeks in stream. For this stream can be very expensive because entire base stream
  157. /// can be dumped into buffering stream if SeekOrigin.End is used.
  158. /// </summary>
  159. /// <param name="offset">A byte offset relative to the origin parameter</param>
  160. /// <param name="origin">A value of type SeekOrigin indicating the reference point used to obtain the new position</param>
  161. /// <returns>The new position within the current stream</returns>
  162. public override long Seek(long offset, SeekOrigin origin)
  163. {
  164. // Check if base stream is seekable
  165. if (baseStream.CanSeek)
  166. return baseStream.Seek(offset, origin);
  167. if (SeekOrigin.Begin == origin)
  168. {
  169. // Just set the absolute position using Position property
  170. Position = offset;
  171. return Position;
  172. }
  173. if (SeekOrigin.Current == origin)
  174. {
  175. // Set the position using current Position property value plus offset
  176. Position = Position + offset;
  177. return Position;
  178. }
  179. if (SeekOrigin.End == origin)
  180. {
  181. //
  182. // Need to read all remaining not read bytes from the base stream to the
  183. // buffering stream. We can't use offset here because stream size may not
  184. // be available because it's not seekable. Then we'll set the position
  185. // based on buffering stream size.
  186. //
  187. // Set position to the last byte in the buffering stream
  188. bufferingStream.Seek(0, SeekOrigin.End);
  189. // Read all remaining bytes from the base stream to the buffering stream
  190. byte [] buffer = new byte[ 4096 ];
  191. for (;;)
  192. {
  193. // Read buffer from base stream
  194. int bytesRead = baseStream.Read(buffer, 0, buffer.Length);
  195. // Break the reading loop if the base stream is exhausted
  196. if (0 == bytesRead)
  197. break;
  198. // Write buffer to the buffering stream
  199. bufferingStream.Write(buffer, 0, bytesRead);
  200. }
  201. // Now buffering stream size is equal to the base stream size. Set position
  202. // using begin origin
  203. Position = bufferingStream.Length - offset;
  204. return Position;
  205. }
  206. throw new NotSupportedException("Not supported SeekOrigin");
  207. }
  208. /// <summary>
  209. /// Gets the length in bytes of the stream. For this stream can be very expensive
  210. /// because entire base stream will be dumped into buffering stream.
  211. /// </summary>
  212. public override long Length
  213. {
  214. get
  215. {
  216. // Check if base stream is seekable
  217. if (baseStream.CanSeek)
  218. return baseStream.Length;
  219. // Preserve the current stream position
  220. long position = Position;
  221. // Seek to the end of stream
  222. Seek(0, SeekOrigin.End);
  223. // Length will be equal to the current position
  224. long length = Position;
  225. // Restore the current stream position
  226. Position = position;
  227. return length;
  228. }
  229. }
  230. /// <summary>
  231. /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
  232. /// </summary>
  233. /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count- 1) replaced by the bytes read from the current source</param>
  234. /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream</param>
  235. /// <param name="count">The maximum number of bytes to be read from the current stream</param>
  236. /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached</returns>
  237. public override int Read(byte[] buffer, int offset, int count)
  238. {
  239. // Check if base stream is seekable
  240. if (baseStream.CanSeek)
  241. return baseStream.Read(buffer, offset, count);
  242. int bytesReadTotal = 0;
  243. // Check if buffering stream has some bytes to read, starting from the
  244. // current position
  245. if (bufferingStream.Length > bufferingStream.Position)
  246. {
  247. // Read available bytes in buffering stream or count bytes to the buffer, whichever is less
  248. bytesReadTotal = bufferingStream.Read(buffer, offset, (int) Math.Min(bufferingStream.Length - bufferingStream.Position, count));
  249. // Account for bytes read from the buffering stream
  250. count -= bytesReadTotal;
  251. offset += bytesReadTotal;
  252. }
  253. // Check if we have any more bytes to read
  254. if (count > 0)
  255. {
  256. Debug.Assert(bufferingStream.Position == bufferingStream.Length);
  257. //
  258. // At this point, buffering stream has position set to its end. We need to read buffer from
  259. // the base stream and write it to the buffering stream
  260. //
  261. // Read count bytes from the base stream starting from offset
  262. int bytesRead = baseStream.Read(buffer, offset, count);
  263. // Check if bytes were really read
  264. if (bytesRead > 0)
  265. {
  266. // Write number of read bytes to the buffering stream starting from offset in buffer
  267. bufferingStream.Write(buffer, offset, bytesRead);
  268. }
  269. // Add number of bytes read at this step to the number of totally read bytes
  270. bytesReadTotal += bytesRead;
  271. }
  272. return bytesReadTotal;
  273. }
  274. /// <summary>
  275. /// Writes to stream.
  276. /// </summary>
  277. /// <param name="buffer">Buffer to write to stream</param>
  278. /// <param name="offset">Stream offset to start write from</param>
  279. /// <param name="count">Number of bytes from buffer to write</param>
  280. /// <exception cref="NotSupportedException">Is thrown always</exception>
  281. public override void Write(byte[] buffer, int offset, int count)
  282. {
  283. throw new NotSupportedException();
  284. }
  285. /// <summary>
  286. /// Set stream length.
  287. /// </summary>
  288. /// <param name="value">Stream length</param>
  289. /// <exception cref="NotSupportedException">Is thrown always</exception>
  290. public override void SetLength(long value)
  291. {
  292. throw new NotSupportedException();
  293. }
  294. /// <summary>
  295. /// Closes base and buffering streams.
  296. /// </summary>
  297. public override void Close()
  298. {
  299. // Close underlying streams
  300. baseStream.Close();
  301. bufferingStream.Close();
  302. }
  303. /// <summary>
  304. /// Flushes the stream.
  305. /// </summary>
  306. public override void Flush()
  307. {
  308. // Flush the buffering stream
  309. bufferingStream.Flush();
  310. }
  311. private Stream baseStream;
  312. private Stream bufferingStream;
  313. }
  314. }