RawBsonArray.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. /* Copyright 2010-2015 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;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using MongoDB.Bson.IO;
  20. using MongoDB.Bson.Serialization;
  21. using MongoDB.Bson.Serialization.Attributes;
  22. using MongoDB.Bson.Serialization.Serializers;
  23. namespace MongoDB.Bson
  24. {
  25. /// <summary>
  26. /// Represents an immutable BSON array that is represented using only the raw bytes.
  27. /// </summary>
  28. [BsonSerializer(typeof(RawBsonArraySerializer))]
  29. public class RawBsonArray : BsonArray, IDisposable
  30. {
  31. // private fields
  32. private bool _disposed;
  33. private IByteBuffer _slice;
  34. private List<IDisposable> _disposableItems = new List<IDisposable>();
  35. private BsonBinaryReaderSettings _readerSettings = BsonBinaryReaderSettings.Defaults;
  36. // constructors
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="RawBsonArray"/> class.
  39. /// </summary>
  40. /// <param name="slice">The slice.</param>
  41. /// <exception cref="System.ArgumentNullException">slice</exception>
  42. /// <exception cref="System.ArgumentException">RawBsonArray cannot be used with an IByteBuffer that needs disposing.</exception>
  43. public RawBsonArray(IByteBuffer slice)
  44. {
  45. if (slice == null)
  46. {
  47. throw new ArgumentNullException("slice");
  48. }
  49. _slice = slice;
  50. }
  51. // public properties
  52. /// <summary>
  53. /// Gets or sets the total number of elements the internal data structure can hold without resizing.
  54. /// </summary>
  55. public override int Capacity
  56. {
  57. get
  58. {
  59. ThrowIfDisposed();
  60. return _slice.Capacity;
  61. }
  62. set
  63. {
  64. throw new NotSupportedException("RawBsonArray instances are immutable.");
  65. }
  66. }
  67. /// <summary>
  68. /// Gets the count of array elements.
  69. /// </summary>
  70. public override int Count
  71. {
  72. get
  73. {
  74. ThrowIfDisposed();
  75. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  76. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  77. {
  78. var count = 0;
  79. bsonReader.ReadStartDocument();
  80. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  81. {
  82. bsonReader.SkipName();
  83. bsonReader.SkipValue();
  84. count++;
  85. }
  86. bsonReader.ReadEndDocument();
  87. return count;
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// Gets whether the array is read-only.
  93. /// </summary>
  94. public override bool IsReadOnly
  95. {
  96. get { return true; }
  97. }
  98. /// <summary>
  99. /// Gets the array elements as raw values (see BsonValue.RawValue).
  100. /// </summary>
  101. [Obsolete("Use ToArray to ToList instead.")]
  102. public override IEnumerable<object> RawValues
  103. {
  104. get
  105. {
  106. ThrowIfDisposed();
  107. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  108. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  109. {
  110. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  111. bsonReader.ReadStartDocument();
  112. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  113. {
  114. bsonReader.SkipName();
  115. yield return DeserializeBsonValue(context).RawValue;
  116. }
  117. bsonReader.ReadEndDocument();
  118. }
  119. }
  120. }
  121. /// <summary>
  122. /// Gets the slice.
  123. /// </summary>
  124. /// <value>
  125. /// The slice.
  126. /// </value>
  127. public IByteBuffer Slice
  128. {
  129. get { return _slice; }
  130. }
  131. /// <summary>
  132. /// Gets the array elements.
  133. /// </summary>
  134. public override IEnumerable<BsonValue> Values
  135. {
  136. get
  137. {
  138. ThrowIfDisposed();
  139. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  140. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  141. {
  142. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  143. bsonReader.ReadStartDocument();
  144. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  145. {
  146. bsonReader.SkipName();
  147. yield return DeserializeBsonValue(context);
  148. }
  149. bsonReader.ReadEndDocument();
  150. }
  151. }
  152. }
  153. // public indexers
  154. /// <summary>
  155. /// Gets or sets a value by position.
  156. /// </summary>
  157. /// <param name="index">The position.</param>
  158. /// <returns>The value.</returns>
  159. public override BsonValue this[int index]
  160. {
  161. get
  162. {
  163. if (index < 0)
  164. {
  165. throw new ArgumentOutOfRangeException("index");
  166. }
  167. ThrowIfDisposed();
  168. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  169. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  170. {
  171. bsonReader.ReadStartDocument();
  172. var i = 0;
  173. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  174. {
  175. bsonReader.SkipName();
  176. if (i == index)
  177. {
  178. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  179. return DeserializeBsonValue(context);
  180. }
  181. bsonReader.SkipValue();
  182. i++;
  183. }
  184. bsonReader.ReadEndDocument();
  185. throw new ArgumentOutOfRangeException("index");
  186. }
  187. }
  188. set
  189. {
  190. throw new NotSupportedException("RawBsonArray instances are immutable.");
  191. }
  192. }
  193. // public methods
  194. /// <summary>
  195. /// Adds an element to the array.
  196. /// </summary>
  197. /// <param name="value">The value to add to the array.</param>
  198. /// <returns>The array (so method calls can be chained).</returns>
  199. public override BsonArray Add(BsonValue value)
  200. {
  201. throw new NotSupportedException("RawBsonArray instances are immutable.");
  202. }
  203. /// <summary>
  204. /// Adds multiple elements to the array.
  205. /// </summary>
  206. /// <param name="values">A list of values to add to the array.</param>
  207. /// <returns>The array (so method calls can be chained).</returns>
  208. public override BsonArray AddRange(IEnumerable<bool> values)
  209. {
  210. throw new NotSupportedException("RawBsonArray instances are immutable.");
  211. }
  212. /// <summary>
  213. /// Adds multiple elements to the array.
  214. /// </summary>
  215. /// <param name="values">A list of values to add to the array.</param>
  216. /// <returns>The array (so method calls can be chained).</returns>
  217. public override BsonArray AddRange(IEnumerable<BsonValue> values)
  218. {
  219. throw new NotSupportedException("RawBsonArray instances are immutable.");
  220. }
  221. /// <summary>
  222. /// Adds multiple elements to the array.
  223. /// </summary>
  224. /// <param name="values">A list of values to add to the array.</param>
  225. /// <returns>The array (so method calls can be chained).</returns>
  226. public override BsonArray AddRange(IEnumerable<DateTime> values)
  227. {
  228. throw new NotSupportedException("RawBsonArray instances are immutable.");
  229. }
  230. /// <summary>
  231. /// Adds multiple elements to the array.
  232. /// </summary>
  233. /// <param name="values">A list of values to add to the array.</param>
  234. /// <returns>The array (so method calls can be chained).</returns>
  235. public override BsonArray AddRange(IEnumerable<double> values)
  236. {
  237. throw new NotSupportedException("RawBsonArray instances are immutable.");
  238. }
  239. /// <summary>
  240. /// Adds multiple elements to the array.
  241. /// </summary>
  242. /// <param name="values">A list of values to add to the array.</param>
  243. /// <returns>The array (so method calls can be chained).</returns>
  244. public override BsonArray AddRange(IEnumerable<int> values)
  245. {
  246. throw new NotSupportedException("RawBsonArray instances are immutable.");
  247. }
  248. /// <summary>
  249. /// Adds multiple elements to the array.
  250. /// </summary>
  251. /// <param name="values">A list of values to add to the array.</param>
  252. /// <returns>The array (so method calls can be chained).</returns>
  253. public override BsonArray AddRange(IEnumerable<long> values)
  254. {
  255. throw new NotSupportedException("RawBsonArray instances are immutable.");
  256. }
  257. /// <summary>
  258. /// Adds multiple elements to the array.
  259. /// </summary>
  260. /// <param name="values">A list of values to add to the array.</param>
  261. /// <returns>The array (so method calls can be chained).</returns>
  262. public override BsonArray AddRange(IEnumerable<ObjectId> values)
  263. {
  264. throw new NotSupportedException("RawBsonArray instances are immutable.");
  265. }
  266. /// <summary>
  267. /// Adds multiple elements to the array.
  268. /// </summary>
  269. /// <param name="values">A list of values to add to the array.</param>
  270. /// <returns>The array (so method calls can be chained).</returns>
  271. public override BsonArray AddRange(IEnumerable<string> values)
  272. {
  273. throw new NotSupportedException("RawBsonArray instances are immutable.");
  274. }
  275. /// <summary>
  276. /// Adds multiple elements to the array.
  277. /// </summary>
  278. /// <param name="values">A list of values to add to the array.</param>
  279. /// <returns>The array (so method calls can be chained).</returns>
  280. public override BsonArray AddRange(IEnumerable values)
  281. {
  282. throw new NotSupportedException("RawBsonArray instances are immutable.");
  283. }
  284. /// <summary>
  285. /// Creates a shallow clone of the array (see also DeepClone).
  286. /// </summary>
  287. /// <returns>A shallow clone of the array.</returns>
  288. public override BsonValue Clone()
  289. {
  290. return new RawBsonArray(CloneSlice());
  291. }
  292. /// <summary>
  293. /// Clears the array.
  294. /// </summary>
  295. public override void Clear()
  296. {
  297. throw new NotSupportedException("RawBsonArray instances are immutable.");
  298. }
  299. /// <summary>
  300. /// Tests whether the array contains a value.
  301. /// </summary>
  302. /// <param name="value">The value to test for.</param>
  303. /// <returns>True if the array contains the value.</returns>
  304. public override bool Contains(BsonValue value)
  305. {
  306. ThrowIfDisposed();
  307. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  308. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  309. {
  310. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  311. bsonReader.ReadStartDocument();
  312. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  313. {
  314. bsonReader.SkipName();
  315. if (DeserializeBsonValue(context).Equals(value))
  316. {
  317. return true;
  318. }
  319. }
  320. bsonReader.ReadEndDocument();
  321. return false;
  322. }
  323. }
  324. /// <summary>
  325. /// Copies elements from this array to another array.
  326. /// </summary>
  327. /// <param name="array">The other array.</param>
  328. /// <param name="arrayIndex">The zero based index of the other array at which to start copying.</param>
  329. public override void CopyTo(BsonValue[] array, int arrayIndex)
  330. {
  331. ThrowIfDisposed();
  332. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  333. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  334. {
  335. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  336. bsonReader.ReadStartDocument();
  337. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  338. {
  339. bsonReader.SkipName();
  340. array[arrayIndex++] = DeserializeBsonValue(context);
  341. }
  342. bsonReader.ReadEndDocument();
  343. }
  344. }
  345. /// <summary>
  346. /// Copies elements from this array to another array as raw values (see BsonValue.RawValue).
  347. /// </summary>
  348. /// <param name="array">The other array.</param>
  349. /// <param name="arrayIndex">The zero based index of the other array at which to start copying.</param>
  350. [Obsolete("Use ToArray or ToList instead.")]
  351. public override void CopyTo(object[] array, int arrayIndex)
  352. {
  353. ThrowIfDisposed();
  354. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  355. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  356. {
  357. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  358. bsonReader.ReadStartDocument();
  359. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  360. {
  361. bsonReader.SkipName();
  362. array[arrayIndex++] = DeserializeBsonValue(context).RawValue;
  363. }
  364. bsonReader.ReadEndDocument();
  365. }
  366. }
  367. /// <summary>
  368. /// Creates a deep clone of the array (see also Clone).
  369. /// </summary>
  370. /// <returns>A deep clone of the array.</returns>
  371. public override BsonValue DeepClone()
  372. {
  373. ThrowIfDisposed();
  374. return new RawBsonArray(CloneSlice());
  375. }
  376. /// <summary>
  377. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  378. /// </summary>
  379. public void Dispose()
  380. {
  381. Dispose(true);
  382. GC.SuppressFinalize(this);
  383. }
  384. /// <summary>
  385. /// Gets an enumerator that can enumerate the elements of the array.
  386. /// </summary>
  387. /// <returns>An enumerator.</returns>
  388. public override IEnumerator<BsonValue> GetEnumerator()
  389. {
  390. ThrowIfDisposed();
  391. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  392. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  393. {
  394. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  395. bsonReader.ReadStartDocument();
  396. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  397. {
  398. bsonReader.SkipName();
  399. yield return DeserializeBsonValue(context);
  400. }
  401. bsonReader.ReadEndDocument();
  402. }
  403. }
  404. /// <summary>
  405. /// Gets the index of a value in the array.
  406. /// </summary>
  407. /// <param name="value">The value to search for.</param>
  408. /// <returns>The zero based index of the value (or -1 if not found).</returns>
  409. public override int IndexOf(BsonValue value)
  410. {
  411. return IndexOf(value, 0, int.MaxValue);
  412. }
  413. /// <summary>
  414. /// Gets the index of a value in the array.
  415. /// </summary>
  416. /// <param name="value">The value to search for.</param>
  417. /// <param name="index">The zero based index at which to start the search.</param>
  418. /// <returns>The zero based index of the value (or -1 if not found).</returns>
  419. public override int IndexOf(BsonValue value, int index)
  420. {
  421. return IndexOf(value, index, int.MaxValue);
  422. }
  423. /// <summary>
  424. /// Gets the index of a value in the array.
  425. /// </summary>
  426. /// <param name="value">The value to search for.</param>
  427. /// <param name="index">The zero based index at which to start the search.</param>
  428. /// <param name="count">The number of elements to search.</param>
  429. /// <returns>The zero based index of the value (or -1 if not found).</returns>
  430. public override int IndexOf(BsonValue value, int index, int count)
  431. {
  432. ThrowIfDisposed();
  433. using (var stream = new ByteBufferStream(_slice, ownsBuffer: false))
  434. using (var bsonReader = new BsonBinaryReader(stream, _readerSettings))
  435. {
  436. var context = BsonDeserializationContext.CreateRoot(bsonReader);
  437. bsonReader.ReadStartDocument();
  438. var i = 0;
  439. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  440. {
  441. bsonReader.SkipName();
  442. if (i >= index)
  443. {
  444. if (count == 0)
  445. {
  446. return -1;
  447. }
  448. if (DeserializeBsonValue(context).Equals(value))
  449. {
  450. return i;
  451. }
  452. count--;
  453. }
  454. else
  455. {
  456. bsonReader.SkipValue();
  457. }
  458. i++;
  459. }
  460. bsonReader.ReadEndDocument();
  461. return -1;
  462. }
  463. }
  464. /// <summary>
  465. /// Inserts a new value into the array.
  466. /// </summary>
  467. /// <param name="index">The zero based index at which to insert the new value.</param>
  468. /// <param name="value">The new value.</param>
  469. public override void Insert(int index, BsonValue value)
  470. {
  471. throw new NotSupportedException("RawBsonArray instances are immutable.");
  472. }
  473. /// <summary>
  474. /// Removes the first occurrence of a value from the array.
  475. /// </summary>
  476. /// <param name="value">The value to remove.</param>
  477. /// <returns>True if the value was removed.</returns>
  478. public override bool Remove(BsonValue value)
  479. {
  480. throw new NotSupportedException("RawBsonArray instances are immutable.");
  481. }
  482. /// <summary>
  483. /// Removes an element from the array.
  484. /// </summary>
  485. /// <param name="index">The zero based index of the element to remove.</param>
  486. public override void RemoveAt(int index)
  487. {
  488. throw new NotSupportedException("RawBsonArray instances are immutable.");
  489. }
  490. /// <summary>
  491. /// Converts the BsonArray to an array of BsonValues.
  492. /// </summary>
  493. /// <returns>An array of BsonValues.</returns>
  494. public override BsonValue[] ToArray()
  495. {
  496. ThrowIfDisposed();
  497. return Values.ToArray();
  498. }
  499. /// <summary>
  500. /// Converts the BsonArray to a list of BsonValues.
  501. /// </summary>
  502. /// <returns>A list of BsonValues.</returns>
  503. public override List<BsonValue> ToList()
  504. {
  505. ThrowIfDisposed();
  506. return Values.ToList();
  507. }
  508. /// <summary>
  509. /// Returns a string representation of the array.
  510. /// </summary>
  511. /// <returns>A string representation of the array.</returns>
  512. public override string ToString()
  513. {
  514. ThrowIfDisposed();
  515. var parts = Values.Select(v => v.ToString()).ToArray();
  516. return string.Format("[{0}]", string.Join(", ", parts));
  517. }
  518. // protected methods
  519. /// <summary>
  520. /// Releases unmanaged and - optionally - managed resources.
  521. /// </summary>
  522. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  523. protected virtual void Dispose(bool disposing)
  524. {
  525. if (!_disposed)
  526. {
  527. if (disposing)
  528. {
  529. if (_slice != null)
  530. {
  531. _slice.Dispose();
  532. _slice = null;
  533. }
  534. if (_disposableItems != null)
  535. {
  536. _disposableItems.ForEach(x => x.Dispose());
  537. _disposableItems = null;
  538. }
  539. }
  540. _disposed = true;
  541. }
  542. }
  543. /// <summary>
  544. /// Throws if disposed.
  545. /// </summary>
  546. /// <exception cref="System.ObjectDisposedException"></exception>
  547. protected void ThrowIfDisposed()
  548. {
  549. if (_disposed)
  550. {
  551. throw new ObjectDisposedException(GetType().Name);
  552. }
  553. }
  554. // private methods
  555. private IByteBuffer CloneSlice()
  556. {
  557. return _slice.GetSlice(0, _slice.Length);
  558. }
  559. private RawBsonArray DeserializeRawBsonArray(IBsonReader bsonReader)
  560. {
  561. var slice = bsonReader.ReadRawBsonArray();
  562. var nestedArray = new RawBsonArray(slice);
  563. _disposableItems.Add(nestedArray);
  564. return nestedArray;
  565. }
  566. private RawBsonDocument DeserializeRawBsonDocument(IBsonReader bsonReader)
  567. {
  568. var slice = bsonReader.ReadRawBsonDocument();
  569. var nestedDocument = new RawBsonDocument(slice);
  570. _disposableItems.Add(nestedDocument);
  571. return nestedDocument;
  572. }
  573. private BsonValue DeserializeBsonValue(BsonDeserializationContext context)
  574. {
  575. var bsonReader = context.Reader;
  576. switch (bsonReader.GetCurrentBsonType())
  577. {
  578. case BsonType.Array: return DeserializeRawBsonArray(bsonReader);
  579. case BsonType.Document: return DeserializeRawBsonDocument(bsonReader);
  580. default: return BsonValueSerializer.Instance.Deserialize(context);
  581. }
  582. }
  583. }
  584. }