ByteArrayBuffer.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /* Copyright 2010-2014 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.IO;
  17. namespace MongoDB.Bson.IO
  18. {
  19. /// <summary>
  20. /// A BSON buffer that is backed by a byte array.
  21. /// </summary>
  22. public class ByteArrayBuffer : IByteBuffer
  23. {
  24. // private fields
  25. private bool _disposed;
  26. private byte[] _bytes;
  27. private int _sliceOffset;
  28. private int _capacity;
  29. private int _length;
  30. private int _position;
  31. private bool _isReadOnly;
  32. // constructors
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="ByteArrayBuffer"/> class.
  35. /// </summary>
  36. /// <param name="bytes">The backing bytes.</param>
  37. /// <param name="sliceOffset">The offset where the slice begins.</param>
  38. /// <param name="length">The length of the slice.</param>
  39. /// <param name="isReadOnly">Whether the buffer is read only.</param>
  40. /// <exception cref="System.ArgumentNullException">bytes</exception>
  41. public ByteArrayBuffer(byte[] bytes, int sliceOffset, int length, bool isReadOnly)
  42. {
  43. if (bytes == null)
  44. {
  45. throw new ArgumentNullException("bytes");
  46. }
  47. _bytes = bytes;
  48. _sliceOffset = sliceOffset;
  49. _capacity = isReadOnly ? length : bytes.Length - _sliceOffset;
  50. _length = length;
  51. _isReadOnly = isReadOnly;
  52. _position = 0;
  53. }
  54. // public properties
  55. /// <summary>
  56. /// Gets or sets the capacity.
  57. /// </summary>
  58. /// <value>
  59. /// The capacity.
  60. /// </value>
  61. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  62. /// <exception cref="System.NotSupportedException">The capacity of a ByteArrayBuffer cannot be changed.</exception>
  63. public int Capacity
  64. {
  65. get
  66. {
  67. ThrowIfDisposed();
  68. return _capacity;
  69. }
  70. set
  71. {
  72. ThrowIfDisposed();
  73. throw new NotSupportedException("The capacity of a ByteArrayBuffer cannot be changed.");
  74. }
  75. }
  76. /// <summary>
  77. /// Gets a value indicating whether this instance is read only.
  78. /// </summary>
  79. /// <value>
  80. /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
  81. /// </value>
  82. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  83. public bool IsReadOnly
  84. {
  85. get
  86. {
  87. ThrowIfDisposed();
  88. return _isReadOnly;
  89. }
  90. }
  91. /// <summary>
  92. /// Gets or sets the length.
  93. /// </summary>
  94. /// <value>
  95. /// The length.
  96. /// </value>
  97. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  98. /// <exception cref="System.InvalidOperationException">The length of a read only buffer cannot be changed.</exception>
  99. /// <exception cref="System.ArgumentOutOfRangeException">Length</exception>
  100. public int Length
  101. {
  102. get
  103. {
  104. ThrowIfDisposed();
  105. return _length;
  106. }
  107. set
  108. {
  109. ThrowIfDisposed();
  110. EnsureIsWritable();
  111. if (value < 0 || value > _capacity)
  112. {
  113. throw new ArgumentOutOfRangeException("length");
  114. }
  115. _length = value;
  116. if (_position > _length)
  117. {
  118. _position = _length;
  119. }
  120. }
  121. }
  122. /// <summary>
  123. /// Gets or sets the position.
  124. /// </summary>
  125. /// <value>
  126. /// The position.
  127. /// </value>
  128. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  129. /// <exception cref="System.ArgumentOutOfRangeException">Position</exception>
  130. public int Position
  131. {
  132. get
  133. {
  134. ThrowIfDisposed();
  135. return _position;
  136. }
  137. set
  138. {
  139. ThrowIfDisposed();
  140. if (value < 0 || value > _capacity)
  141. {
  142. throw new ArgumentOutOfRangeException("Position");
  143. }
  144. _position = value;
  145. if (_length < _position)
  146. {
  147. _length = _position;
  148. }
  149. }
  150. }
  151. // protected properties
  152. /// <summary>
  153. /// Gets a value indicating whether this <see cref="ByteArrayBuffer"/> is disposed.
  154. /// </summary>
  155. /// <value>
  156. /// <c>true</c> if disposed; otherwise, <c>false</c>.
  157. /// </value>
  158. protected bool Disposed
  159. {
  160. get { return _disposed; }
  161. }
  162. /// <summary>
  163. /// Gets the slice offset.
  164. /// </summary>
  165. /// <value>
  166. /// The slice offset.
  167. /// </value>
  168. protected int SliceOffset
  169. {
  170. get { return _sliceOffset; }
  171. }
  172. // public methods
  173. /// <summary>
  174. /// Clears this instance.
  175. /// </summary>
  176. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  177. /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
  178. public virtual void Clear()
  179. {
  180. ThrowIfDisposed();
  181. EnsureIsWritable();
  182. _position = 0;
  183. _length = 0;
  184. }
  185. /// <summary>
  186. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  187. /// </summary>
  188. public void Dispose()
  189. {
  190. Dispose(true);
  191. GC.SuppressFinalize(this);
  192. }
  193. /// <summary>
  194. /// Finds the next null byte.
  195. /// </summary>
  196. /// <returns>
  197. /// The position of the next null byte.
  198. /// </returns>
  199. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  200. public int FindNullByte()
  201. {
  202. ThrowIfDisposed();
  203. var index = Array.IndexOf<byte>(_bytes, 0, _sliceOffset + _position, _length - _position);
  204. return (index == -1) ? -1 : index - _sliceOffset;
  205. }
  206. /// <summary>
  207. /// Gets a slice of this buffer.
  208. /// </summary>
  209. /// <param name="position">The position of the start of the slice.</param>
  210. /// <param name="length">The length of the slice.</param>
  211. /// <returns>
  212. /// A slice of this buffer.
  213. /// </returns>
  214. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  215. /// <exception cref="System.InvalidOperationException">GetSlice can only be called for read only buffers.</exception>
  216. /// <exception cref="System.ArgumentOutOfRangeException">
  217. /// position
  218. /// or
  219. /// length
  220. /// </exception>
  221. public virtual IByteBuffer GetSlice(int position, int length)
  222. {
  223. ThrowIfDisposed();
  224. EnsureIsReadOnly();
  225. if (position < 0 || position >= _length)
  226. {
  227. throw new ArgumentOutOfRangeException("position");
  228. }
  229. if (length <= 0 || length > _length - position)
  230. {
  231. throw new ArgumentOutOfRangeException("length");
  232. }
  233. return new ByteArrayBuffer(_bytes, _sliceOffset + position, length, true);
  234. }
  235. /// <summary>
  236. /// Loads the buffer from a stream.
  237. /// </summary>
  238. /// <param name="stream">The stream.</param>
  239. /// <param name="count">The count.</param>
  240. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  241. /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
  242. /// <exception cref="System.ArgumentNullException">stream</exception>
  243. /// <exception cref="System.ArgumentOutOfRangeException">count</exception>
  244. public void LoadFrom(Stream stream, int count)
  245. {
  246. ThrowIfDisposed();
  247. EnsureIsWritable();
  248. if (stream == null)
  249. {
  250. throw new ArgumentNullException("stream");
  251. }
  252. if (count > _capacity - _position)
  253. {
  254. throw new ArgumentOutOfRangeException("count");
  255. }
  256. EnsureSpaceAvailable(count);
  257. var position = _position; // don't advance position
  258. while (count > 0)
  259. {
  260. var bytesRead = stream.Read(_bytes, _sliceOffset + position, count);
  261. if (bytesRead == 0)
  262. {
  263. throw new EndOfStreamException();
  264. }
  265. position += bytesRead;
  266. count -= bytesRead;
  267. }
  268. if (_length < position)
  269. {
  270. _length = position;
  271. }
  272. }
  273. /// <summary>
  274. /// Makes this buffer read only.
  275. /// </summary>
  276. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  277. public void MakeReadOnly()
  278. {
  279. ThrowIfDisposed();
  280. _isReadOnly = true;
  281. }
  282. /// <summary>
  283. /// Read directly from the backing bytes. The returned ArraySegment points directly to the backing bytes for
  284. /// the current position and you can read the bytes directly from there. If the backing bytes happen to span
  285. /// a chunk boundary shortly after the current position there might not be enough bytes left in the current
  286. /// chunk in which case the returned ArraySegment will have a Count of zero and you should call ReadBytes instead.
  287. ///
  288. /// When ReadBackingBytes returns the position will have been advanced by count bytes *if and only if* there
  289. /// were count bytes left in the current chunk.
  290. /// </summary>
  291. /// <param name="count">The number of bytes you need to read.</param>
  292. /// <returns>
  293. /// An ArraySegment pointing directly to the backing bytes for the current position.
  294. /// </returns>
  295. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  296. public ArraySegment<byte> ReadBackingBytes(int count)
  297. {
  298. ThrowIfDisposed();
  299. EnsureDataAvailable(count);
  300. var offset = _sliceOffset + _position;
  301. _position += count;
  302. return new ArraySegment<byte>(_bytes, offset, count);
  303. }
  304. /// <summary>
  305. /// Reads a byte.
  306. /// </summary>
  307. /// <returns>
  308. /// A byte.
  309. /// </returns>
  310. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  311. public byte ReadByte()
  312. {
  313. ThrowIfDisposed();
  314. EnsureDataAvailable(1);
  315. return _bytes[_sliceOffset + _position++];
  316. }
  317. /// <summary>
  318. /// Reads bytes.
  319. /// </summary>
  320. /// <param name="destination">The destination.</param>
  321. /// <param name="destinationOffset">The destination offset.</param>
  322. /// <param name="count">The count.</param>
  323. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  324. public void ReadBytes(byte[] destination, int destinationOffset, int count)
  325. {
  326. ThrowIfDisposed();
  327. EnsureDataAvailable(count);
  328. Buffer.BlockCopy(_bytes, _sliceOffset + _position, destination, destinationOffset, count);
  329. _position += count;
  330. }
  331. /// <summary>
  332. /// Reads bytes.
  333. /// </summary>
  334. /// <param name="count">The count.</param>
  335. /// <returns>
  336. /// The bytes.
  337. /// </returns>
  338. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  339. public byte[] ReadBytes(int count)
  340. {
  341. ThrowIfDisposed();
  342. var destination = new byte[count];
  343. ReadBytes(destination, 0, count);
  344. return destination;
  345. }
  346. /// <summary>
  347. /// Write directly to the backing bytes. The returned ArraySegment points directly to the backing bytes for
  348. /// the current position and you can write the bytes directly to there. If the backing bytes happen to span
  349. /// a chunk boundary shortly after the current position there might not be enough bytes left in the current
  350. /// chunk in which case the returned ArraySegment will have a Count of zero and you should call WriteBytes instead.
  351. ///
  352. /// When WriteBackingBytes returns the position has not been advanced. After you have written up to count
  353. /// bytes directly to the backing bytes advance the position by the number of bytes actually written.
  354. /// </summary>
  355. /// <param name="count">The count.</param>
  356. /// <returns>
  357. /// An ArraySegment pointing directly to the backing bytes for the current position.
  358. /// </returns>
  359. public ArraySegment<byte> WriteBackingBytes(int count)
  360. {
  361. ThrowIfDisposed();
  362. EnsureSpaceAvailable(count);
  363. var offset = _sliceOffset + _position;
  364. return new ArraySegment<byte>(_bytes, offset, count);
  365. }
  366. /// <summary>
  367. /// Writes a byte.
  368. /// </summary>
  369. /// <param name="source">The byte.</param>
  370. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  371. /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
  372. public void WriteByte(byte source)
  373. {
  374. ThrowIfDisposed();
  375. EnsureIsWritable();
  376. EnsureSpaceAvailable(1);
  377. _bytes[_sliceOffset + _position++] = source;
  378. if (_length < _position)
  379. {
  380. _length = _position;
  381. }
  382. }
  383. /// <summary>
  384. /// Writes bytes.
  385. /// </summary>
  386. /// <param name="bytes">The bytes.</param>
  387. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  388. /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
  389. public void WriteBytes(byte[] bytes)
  390. {
  391. ThrowIfDisposed();
  392. EnsureIsWritable();
  393. var count = bytes.Length;
  394. EnsureSpaceAvailable(count);
  395. Buffer.BlockCopy(bytes, 0, _bytes, _sliceOffset + _position, count);
  396. _position += count;
  397. if (_length < _position)
  398. {
  399. _length = _position;
  400. }
  401. }
  402. /// <summary>
  403. /// Writes bytes.
  404. /// </summary>
  405. /// <param name="source">The bytes (in the form of an IByteBuffer).</param>
  406. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  407. /// <exception cref="System.InvalidOperationException">Write operations are not allowed for read only buffers.</exception>
  408. public void WriteBytes(IByteBuffer source)
  409. {
  410. ThrowIfDisposed();
  411. EnsureIsWritable();
  412. var count = source.Length;
  413. EnsureSpaceAvailable(count);
  414. var savedPosition = source.Position;
  415. source.Position = 0;
  416. source.ReadBytes(_bytes, _sliceOffset + _position, count);
  417. _position += count;
  418. if (_length < _position)
  419. {
  420. _length = _position;
  421. }
  422. source.Position = savedPosition;
  423. }
  424. /// <summary>
  425. /// Writes Length bytes from this buffer starting at Position 0 to a stream.
  426. /// </summary>
  427. /// <param name="stream">The stream.</param>
  428. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  429. public void WriteTo(Stream stream)
  430. {
  431. ThrowIfDisposed();
  432. stream.Write(_bytes, _sliceOffset, _length);
  433. }
  434. // protected methods
  435. /// <summary>
  436. /// Releases unmanaged and - optionally - managed resources.
  437. /// </summary>
  438. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  439. protected virtual void Dispose(bool disposing)
  440. {
  441. // subclasses override this method if they have anything to Dispose
  442. _disposed = true;
  443. }
  444. /// <summary>
  445. /// Ensures the buffer is writable.
  446. /// </summary>
  447. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer is not writable.</exception>
  448. protected void EnsureIsWritable()
  449. {
  450. if (_isReadOnly)
  451. {
  452. var message = string.Format("{0} is not writable.", GetType().Name);
  453. throw new InvalidOperationException(message);
  454. }
  455. }
  456. /// <summary>
  457. /// Ensures the buffer is read only.
  458. /// </summary>
  459. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer is not read only.</exception>
  460. protected void EnsureIsReadOnly()
  461. {
  462. if (!_isReadOnly)
  463. {
  464. var message = string.Format("{0} is not read only.", GetType().Name);
  465. throw new InvalidOperationException(message);
  466. }
  467. }
  468. /// <summary>
  469. /// Throws if disposed.
  470. /// </summary>
  471. /// <exception cref="System.ObjectDisposedException"></exception>
  472. protected void ThrowIfDisposed()
  473. {
  474. if (_disposed)
  475. {
  476. throw new ObjectDisposedException(GetType().Name);
  477. }
  478. }
  479. // private methods
  480. private void EnsureDataAvailable(int needed)
  481. {
  482. if (needed > _length - _position)
  483. {
  484. var available = _length - _position;
  485. var message = string.Format(
  486. "Not enough input bytes available. Needed {0}, but only {1} are available (at position {2}).",
  487. needed, available, _position);
  488. throw new EndOfStreamException(message);
  489. }
  490. }
  491. private void EnsureSpaceAvailable(int needed)
  492. {
  493. if (needed > _capacity - _length)
  494. {
  495. var available = _capacity - _length;
  496. var message = string.Format(
  497. "Not enough space available. Needed {0}, but only {1} are available (at position {2}).",
  498. needed, available, _position);
  499. throw new EndOfStreamException(message);
  500. }
  501. }
  502. }
  503. }