MultiChunkBuffer.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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.Collections.Generic;
  17. using System.IO;
  18. using System.Linq;
  19. namespace MongoDB.Bson.IO
  20. {
  21. /// <summary>
  22. /// An IBsonBuffer that has multiple chunks.
  23. /// </summary>
  24. public class MultiChunkBuffer : IByteBuffer
  25. {
  26. // private fields
  27. private readonly BsonChunkPool _chunkPool;
  28. private readonly int _chunkSize;
  29. private readonly int _sliceOffset;
  30. private int _capacity;
  31. private List<BsonChunk> _chunks;
  32. private bool _disposed;
  33. private bool _isReadOnly;
  34. private int _length;
  35. private int _position;
  36. // constructors
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="MultiChunkBuffer"/> class.
  39. /// </summary>
  40. /// <param name="chunkPool">The chunk pool.</param>
  41. /// <exception cref="System.ArgumentNullException">chunkPool</exception>
  42. public MultiChunkBuffer(BsonChunkPool chunkPool)
  43. {
  44. if (chunkPool == null)
  45. {
  46. throw new ArgumentNullException("chunkPool");
  47. }
  48. _chunkPool = chunkPool;
  49. _chunks = new List<BsonChunk>();
  50. _chunkSize = chunkPool.ChunkSize;
  51. _sliceOffset = 0;
  52. _capacity = 0; // EnsureSpaceAvailable will add capacity as needed
  53. _length = 0;
  54. _position = 0;
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="MultiChunkBuffer"/> class.
  58. /// </summary>
  59. /// <param name="chunks">The chunks.</param>
  60. /// <param name="sliceOffset">The slice offset.</param>
  61. /// <param name="length">The length.</param>
  62. /// <param name="isReadOnly">Whether the buffer is read only.</param>
  63. /// <exception cref="System.ArgumentNullException">chunks</exception>
  64. internal MultiChunkBuffer(IEnumerable<BsonChunk> chunks, int sliceOffset, int length, bool isReadOnly)
  65. {
  66. if (chunks == null)
  67. {
  68. throw new ArgumentNullException("chunks");
  69. }
  70. _chunks = new List<BsonChunk>(chunks);
  71. if (_chunks.Count == 0)
  72. {
  73. throw new ArgumentException("No chunks where provided.", "chunks");
  74. }
  75. _chunkSize = _chunks[0].Bytes.Length;
  76. foreach (var chunk in _chunks)
  77. {
  78. if (chunk.Bytes.Length != _chunkSize) { throw new ArgumentException("The chunks are not all the same size."); }
  79. }
  80. if (sliceOffset < 0)
  81. {
  82. throw new ArgumentOutOfRangeException("sliceOffset");
  83. }
  84. _sliceOffset = sliceOffset;
  85. var maxCapacity = _chunks.Count * _chunkSize - _sliceOffset;
  86. if (length < 0 || length > maxCapacity)
  87. {
  88. throw new ArgumentOutOfRangeException("length");
  89. }
  90. _capacity = isReadOnly ? length : maxCapacity; // the capacity is fixed
  91. _length = length;
  92. _chunkPool = null;
  93. _isReadOnly = isReadOnly;
  94. _position = 0;
  95. foreach (var chunk in _chunks)
  96. {
  97. chunk.IncrementReferenceCount();
  98. }
  99. }
  100. // public properties
  101. /// <summary>
  102. /// Gets or sets the capacity.
  103. /// </summary>
  104. /// <value>
  105. /// The capacity.
  106. /// </value>
  107. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  108. /// <exception cref="System.NotSupportedException">The capacity of a MultiChunkBuffer cannot be changed.</exception>
  109. public int Capacity
  110. {
  111. get
  112. {
  113. ThrowIfDisposed();
  114. return _capacity;
  115. }
  116. set
  117. {
  118. ThrowIfDisposed();
  119. EnsureIsWritable();
  120. if (value < 0)
  121. {
  122. throw new ArgumentOutOfRangeException("Capacity");
  123. }
  124. if (value < _capacity)
  125. {
  126. ShrinkCapacity(value);
  127. }
  128. else if (value > _capacity)
  129. {
  130. ExpandCapacity(value);
  131. }
  132. }
  133. }
  134. /// <summary>
  135. /// Gets a value indicating whether this instance is read only.
  136. /// </summary>
  137. /// <value>
  138. /// <c>true</c> if this instance is read only; otherwise, <c>false</c>.
  139. /// </value>
  140. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  141. public bool IsReadOnly
  142. {
  143. get
  144. {
  145. ThrowIfDisposed();
  146. return _isReadOnly;
  147. }
  148. }
  149. /// <summary>
  150. /// Gets or sets the length.
  151. /// </summary>
  152. /// <value>
  153. /// The length.
  154. /// </value>
  155. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  156. /// <exception cref="System.ArgumentOutOfRangeException">Length</exception>
  157. /// <exception cref="System.InvalidOperationException">The length of a read only buffer cannot be changed.</exception>
  158. public int Length
  159. {
  160. get
  161. {
  162. ThrowIfDisposed();
  163. return _length;
  164. }
  165. set
  166. {
  167. ThrowIfDisposed();
  168. if (value < 0 || value > _capacity)
  169. {
  170. throw new ArgumentOutOfRangeException("Length");
  171. }
  172. EnsureIsWritable();
  173. EnsureSpaceAvailable(value - _position);
  174. _length = value;
  175. if (_position > _length)
  176. {
  177. _position = _length;
  178. }
  179. }
  180. }
  181. /// <summary>
  182. /// Gets or sets the position.
  183. /// </summary>
  184. /// <value>
  185. /// The position.
  186. /// </value>
  187. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  188. /// <exception cref="System.ArgumentOutOfRangeException">Position</exception>
  189. public int Position
  190. {
  191. get
  192. {
  193. ThrowIfDisposed();
  194. return _position;
  195. }
  196. set
  197. {
  198. ThrowIfDisposed();
  199. if (value < 0 || value > _capacity)
  200. {
  201. throw new ArgumentOutOfRangeException("Position");
  202. }
  203. EnsureSpaceAvailable(value - _position);
  204. _position = value;
  205. if (_length < _position)
  206. {
  207. _length = _position;
  208. }
  209. }
  210. }
  211. // public methods
  212. /// <summary>
  213. /// Clears this instance.
  214. /// </summary>
  215. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  216. /// <exception cref="System.InvalidOperationException">The MultiChunkBuffer is read only.</exception>
  217. public void Clear()
  218. {
  219. ThrowIfDisposed();
  220. EnsureIsWritable();
  221. ShrinkCapacity(0);
  222. }
  223. /// <summary>
  224. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  225. /// </summary>
  226. public void Dispose()
  227. {
  228. Dispose(true);
  229. GC.SuppressFinalize(this);
  230. }
  231. /// <summary>
  232. /// Finds the next null byte.
  233. /// </summary>
  234. /// <returns>
  235. /// The position of the next null byte.
  236. /// </returns>
  237. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  238. public int FindNullByte()
  239. {
  240. ThrowIfDisposed();
  241. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  242. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  243. var remaining = _length - _position;
  244. while (remaining > 0)
  245. {
  246. var chunkRemaining = _chunkSize - chunkOffset;
  247. var index = Array.IndexOf<byte>(_chunks[chunkIndex].Bytes, 0, chunkOffset, chunkRemaining);
  248. if (index != -1)
  249. {
  250. return (chunkIndex * _chunkSize + index) - _sliceOffset;
  251. }
  252. chunkIndex += 1;
  253. chunkOffset = 0;
  254. remaining -= chunkRemaining;
  255. }
  256. return -1;
  257. }
  258. /// <summary>
  259. /// Gets a slice of this buffer.
  260. /// </summary>
  261. /// <param name="position">The position of the start of the slice.</param>
  262. /// <param name="length">The length of the slice.</param>
  263. /// <returns>
  264. /// A slice of this buffer.
  265. /// </returns>
  266. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  267. /// <exception cref="System.InvalidOperationException">GetSlice can only be called for read only buffers.</exception>
  268. /// <exception cref="System.ArgumentOutOfRangeException">
  269. /// position
  270. /// or
  271. /// length
  272. /// </exception>
  273. public IByteBuffer GetSlice(int position, int length)
  274. {
  275. ThrowIfDisposed();
  276. EnsureIsReadOnly();
  277. if (position < 0 || position >= _length)
  278. {
  279. throw new ArgumentOutOfRangeException("position");
  280. }
  281. if (length <= 0 || length > _length - position)
  282. {
  283. throw new ArgumentOutOfRangeException("length");
  284. }
  285. var firstChunk = (_sliceOffset + position) / _chunkSize;
  286. var lastChunk = (_sliceOffset + position + length - 1) / _chunkSize;
  287. var sliceOffset = (_sliceOffset + position) - (firstChunk * _chunkSize);
  288. if (firstChunk == lastChunk)
  289. {
  290. return new SingleChunkBuffer(_chunks[firstChunk], sliceOffset, length, true);
  291. }
  292. else
  293. {
  294. var chunks = _chunks.Skip(firstChunk).Take(lastChunk - firstChunk + 1);
  295. return new MultiChunkBuffer(chunks, sliceOffset, length, true);
  296. }
  297. }
  298. /// <summary>
  299. /// Loads the buffer from a stream.
  300. /// </summary>
  301. /// <param name="stream">The stream.</param>
  302. /// <param name="count">The count.</param>
  303. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  304. /// <exception cref="System.InvalidOperationException">The MultiChunkBuffer is read only.</exception>
  305. /// <exception cref="System.ArgumentNullException">stream</exception>
  306. /// <exception cref="System.ArgumentOutOfRangeException">count</exception>
  307. public void LoadFrom(Stream stream, int count)
  308. {
  309. ThrowIfDisposed();
  310. EnsureIsWritable();
  311. if (stream == null)
  312. {
  313. throw new ArgumentNullException("stream");
  314. }
  315. EnsureSpaceAvailable(count);
  316. var position = _position; // don't advance position
  317. while (count > 0)
  318. {
  319. var chunkIndex = (_sliceOffset + position) / _chunkSize;
  320. var chunkOffset = (_sliceOffset + position) % _chunkSize;
  321. var chunkRemaining = _chunkSize - chunkOffset;
  322. var bytesToRead = (count <= chunkRemaining) ? count : chunkRemaining;
  323. var bytesRead = stream.Read(_chunks[chunkIndex].Bytes, chunkOffset, bytesToRead);
  324. if (bytesRead == 0)
  325. {
  326. throw new EndOfStreamException();
  327. }
  328. position += bytesRead;
  329. count -= bytesRead;
  330. }
  331. if (_length < position)
  332. {
  333. _length = position;
  334. }
  335. }
  336. /// <summary>
  337. /// Makes this buffer read only.
  338. /// </summary>
  339. /// <exception cref="System.ObjectDisposedException">ByteArrayBuffer</exception>
  340. public void MakeReadOnly()
  341. {
  342. ThrowIfDisposed();
  343. _isReadOnly = true;
  344. }
  345. /// <summary>
  346. /// Read directly from the backing bytes. The returned ArraySegment points directly to the backing bytes for
  347. /// the current position and you can read the bytes directly from there. If the backing bytes happen to span
  348. /// a chunk boundary shortly after the current position there might not be enough bytes left in the current
  349. /// chunk in which case the returned ArraySegment will have a Count of zero and you should call ReadBytes instead.
  350. ///
  351. /// When ReadBackingBytes returns the position will have been advanced by count bytes *if and only if* there
  352. /// were count bytes left in the current chunk.
  353. /// </summary>
  354. /// <param name="count">The number of bytes you need to read.</param>
  355. /// <returns>
  356. /// An ArraySegment pointing directly to the backing bytes for the current position.
  357. /// </returns>
  358. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  359. public ArraySegment<byte> ReadBackingBytes(int count)
  360. {
  361. ThrowIfDisposed();
  362. EnsureDataAvailable(count);
  363. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  364. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  365. var chunkRemaining = _chunkSize - chunkOffset;
  366. if (count <= chunkRemaining)
  367. {
  368. _position += count;
  369. return new ArraySegment<byte>(_chunks[chunkIndex].Bytes, chunkOffset, count);
  370. }
  371. else
  372. {
  373. return new ArraySegment<byte>();
  374. }
  375. }
  376. /// <summary>
  377. /// Reads a byte.
  378. /// </summary>
  379. /// <returns>
  380. /// A byte.
  381. /// </returns>
  382. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  383. public byte ReadByte()
  384. {
  385. ThrowIfDisposed();
  386. EnsureDataAvailable(1);
  387. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  388. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  389. var value = _chunks[chunkIndex].Bytes[chunkOffset];
  390. _position += 1;
  391. return value;
  392. }
  393. /// <summary>
  394. /// Reads bytes.
  395. /// </summary>
  396. /// <param name="destination">The destination.</param>
  397. /// <param name="destinationOffset">The destination offset.</param>
  398. /// <param name="count">The count.</param>
  399. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  400. public void ReadBytes(byte[] destination, int destinationOffset, int count)
  401. {
  402. ThrowIfDisposed();
  403. EnsureDataAvailable(count);
  404. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  405. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  406. while (count > 0)
  407. {
  408. var chunkRemaining = _chunkSize - chunkOffset;
  409. var bytesToCopy = (count < chunkRemaining) ? count : chunkRemaining;
  410. Buffer.BlockCopy(_chunks[chunkIndex].Bytes, chunkOffset, destination, destinationOffset, bytesToCopy);
  411. chunkIndex += 1;
  412. chunkOffset = 0;
  413. count -= bytesToCopy;
  414. destinationOffset += bytesToCopy;
  415. _position += bytesToCopy;
  416. }
  417. }
  418. /// <summary>
  419. /// Reads bytes.
  420. /// </summary>
  421. /// <param name="count">The count.</param>
  422. /// <returns>
  423. /// The bytes.
  424. /// </returns>
  425. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  426. public byte[] ReadBytes(int count)
  427. {
  428. ThrowIfDisposed();
  429. var destination = new byte[count];
  430. ReadBytes(destination, 0, count);
  431. return destination;
  432. }
  433. /// <summary>
  434. /// Write directly to the backing bytes. The returned ArraySegment points directly to the backing bytes for
  435. /// the current position and you can write the bytes directly to there. If the backing bytes happen to span
  436. /// a chunk boundary shortly after the current position there might not be enough bytes left in the current
  437. /// chunk in which case the returned ArraySegment will have a Count of zero and you should call WriteBytes instead.
  438. ///
  439. /// When WriteBackingBytes returns the position has not been advanced. After you have written up to count
  440. /// bytes directly to the backing bytes advance the position by the number of bytes actually written.
  441. /// </summary>
  442. /// <param name="count">The count.</param>
  443. /// <returns>
  444. /// An ArraySegment pointing directly to the backing bytes for the current position.
  445. /// </returns>
  446. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  447. public ArraySegment<byte> WriteBackingBytes(int count)
  448. {
  449. ThrowIfDisposed();
  450. EnsureSpaceAvailable(count);
  451. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  452. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  453. var chunkRemaining = _chunkSize - chunkOffset;
  454. if (count <= chunkRemaining)
  455. {
  456. return new ArraySegment<byte>(_chunks[chunkIndex].Bytes, chunkOffset, count);
  457. }
  458. else
  459. {
  460. return new ArraySegment<byte>();
  461. }
  462. }
  463. /// <summary>
  464. /// Writes a byte.
  465. /// </summary>
  466. /// <param name="source">The byte.</param>
  467. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  468. /// <exception cref="System.InvalidOperationException">The MultiChunkBuffer is read only.</exception>
  469. public void WriteByte(byte source)
  470. {
  471. ThrowIfDisposed();
  472. EnsureIsWritable();
  473. EnsureSpaceAvailable(1);
  474. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  475. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  476. _chunks[chunkIndex].Bytes[chunkOffset] = source;
  477. _position += 1;
  478. if (_length < _position)
  479. {
  480. _length = _position;
  481. }
  482. }
  483. /// <summary>
  484. /// Writes bytes.
  485. /// </summary>
  486. /// <param name="source">The bytes (in the form of a byte array).</param>
  487. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  488. /// <exception cref="System.InvalidOperationException">The MultiChunkBuffer is read only.</exception>
  489. public void WriteBytes(byte[] source)
  490. {
  491. ThrowIfDisposed();
  492. EnsureIsWritable();
  493. EnsureSpaceAvailable(source.Length);
  494. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  495. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  496. var remaining = source.Length;
  497. var sourceOffset = 0;
  498. while (remaining > 0)
  499. {
  500. var chunkRemaining = _chunkSize - chunkOffset;
  501. var bytesToCopy = (remaining < chunkRemaining) ? remaining : chunkRemaining;
  502. Buffer.BlockCopy(source, sourceOffset, _chunks[chunkIndex].Bytes, chunkOffset, bytesToCopy);
  503. chunkIndex += 1;
  504. chunkOffset = 0;
  505. remaining -= bytesToCopy;
  506. sourceOffset += bytesToCopy;
  507. _position += bytesToCopy;
  508. }
  509. if (_length < _position)
  510. {
  511. _length = _position;
  512. }
  513. }
  514. /// <summary>
  515. /// Writes bytes.
  516. /// </summary>
  517. /// <param name="source">The bytes (in the form of an IByteBuffer).</param>
  518. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  519. /// <exception cref="System.InvalidOperationException">The MultiChunkBuffer is read only.</exception>
  520. public void WriteBytes(IByteBuffer source)
  521. {
  522. ThrowIfDisposed();
  523. EnsureIsWritable();
  524. EnsureSpaceAvailable(source.Length);
  525. var savedPosition = source.Position;
  526. source.Position = 0;
  527. var chunkIndex = (_sliceOffset + _position) / _chunkSize;
  528. var chunkOffset = (_sliceOffset + _position) % _chunkSize;
  529. var remaining = source.Length;
  530. while (remaining > 0)
  531. {
  532. var chunkRemaining = _chunkSize - chunkOffset;
  533. var bytesToCopy = (remaining < chunkRemaining) ? remaining : chunkRemaining;
  534. source.ReadBytes(_chunks[chunkIndex].Bytes, chunkOffset, bytesToCopy);
  535. chunkIndex += 1;
  536. chunkOffset = 0;
  537. remaining -= bytesToCopy;
  538. _position += bytesToCopy;
  539. }
  540. if (_length < _position)
  541. {
  542. _length = _position;
  543. }
  544. source.Position = savedPosition;
  545. }
  546. /// <summary>
  547. /// Writes Length bytes from this buffer starting at Position 0 to a stream.
  548. /// </summary>
  549. /// <param name="stream">The stream.</param>
  550. /// <exception cref="System.ObjectDisposedException">MultiChunkBuffer</exception>
  551. public void WriteTo(Stream stream)
  552. {
  553. ThrowIfDisposed();
  554. var chunkIndex = _sliceOffset / _chunkSize;
  555. var chunkOffset = _sliceOffset % _chunkSize;
  556. var remaining = _length;
  557. while (remaining > 0)
  558. {
  559. var chunkRemaining = _chunkSize - chunkOffset;
  560. var bytesToWrite = (remaining < chunkRemaining) ? remaining : chunkRemaining;
  561. stream.Write(_chunks[chunkIndex].Bytes, chunkOffset, bytesToWrite);
  562. chunkIndex += 1;
  563. chunkOffset = 0;
  564. remaining -= bytesToWrite;
  565. }
  566. }
  567. // protected methods
  568. /// <summary>
  569. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  570. /// </summary>
  571. protected virtual void Dispose(bool disposing)
  572. {
  573. if (!_disposed)
  574. {
  575. if (disposing)
  576. {
  577. if (_chunks != null)
  578. {
  579. foreach (var chunk in _chunks)
  580. {
  581. chunk.DecrementReferenceCount();
  582. }
  583. _chunks = null;
  584. }
  585. }
  586. _disposed = true;
  587. }
  588. }
  589. /// <summary>
  590. /// Throws if disposed.
  591. /// </summary>
  592. /// <exception cref="System.ObjectDisposedException"></exception>
  593. protected void ThrowIfDisposed()
  594. {
  595. if (_disposed)
  596. {
  597. throw new ObjectDisposedException(GetType().Name);
  598. }
  599. }
  600. // private methods
  601. private void EnsureDataAvailable(int needed)
  602. {
  603. if (needed > _length - _position)
  604. {
  605. var available = _length - _position;
  606. var message = string.Format(
  607. "Not enough input bytes available. Needed {0}, but only {1} are available (at position {2}).",
  608. needed, available, _position);
  609. throw new EndOfStreamException(message);
  610. }
  611. }
  612. private void EnsureIsReadOnly()
  613. {
  614. if (!_isReadOnly)
  615. {
  616. throw new InvalidOperationException("MultiChunkBuffer is not read only.");
  617. }
  618. }
  619. private void EnsureIsWritable()
  620. {
  621. if (_isReadOnly)
  622. {
  623. throw new InvalidOperationException("MultiChunkBuffer is not writable.");
  624. }
  625. }
  626. private void EnsureSpaceAvailable(int needed)
  627. {
  628. if (needed > _capacity - _position)
  629. {
  630. var targetCapacity = _position + needed;
  631. ExpandCapacity(targetCapacity);
  632. }
  633. }
  634. private void ExpandCapacity(int targetCapacity)
  635. {
  636. if (_chunkPool == null)
  637. {
  638. throw new InvalidOperationException("Capacity cannot be expanded because this buffer was created without specifying a chunk pool.");
  639. }
  640. while (_capacity < targetCapacity)
  641. {
  642. var chunk = _chunkPool.AcquireChunk();
  643. chunk.IncrementReferenceCount();
  644. _chunks.Add(chunk);
  645. _capacity += _chunkSize;
  646. }
  647. }
  648. private void ShrinkCapacity(int targetCapacity)
  649. {
  650. while (_capacity > targetCapacity && (_capacity - targetCapacity) >= _chunkSize)
  651. {
  652. var lastIndex = _chunks.Count - 1;
  653. _chunks[lastIndex].DecrementReferenceCount();
  654. _chunks.RemoveAt(lastIndex);
  655. _capacity -= _chunkSize;
  656. }
  657. if (_length > targetCapacity)
  658. {
  659. _length = targetCapacity;
  660. }
  661. if (_position > targetCapacity)
  662. {
  663. _position = targetCapacity;
  664. }
  665. }
  666. }
  667. }