BsonBinaryReader.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. /* Copyright 2010-present 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.Globalization;
  17. using System.IO;
  18. namespace MongoDB.Bson.IO
  19. {
  20. /// <summary>
  21. /// Represents a BSON reader for a binary BSON byte array.
  22. /// </summary>
  23. public class BsonBinaryReader : BsonReader
  24. {
  25. // private fields
  26. private readonly Stream _baseStream;
  27. private readonly BsonStream _bsonStream;
  28. private BsonBinaryReaderContext _context;
  29. // constructors
  30. /// <summary>
  31. /// Initializes a new instance of the BsonBinaryReader class.
  32. /// </summary>
  33. /// <param name="stream">A stream (BsonBinary does not own the stream and will not Dispose it).</param>
  34. public BsonBinaryReader(Stream stream)
  35. : this(stream, BsonBinaryReaderSettings.Defaults)
  36. {
  37. }
  38. /// <summary>
  39. /// Initializes a new instance of the BsonBinaryReader class.
  40. /// </summary>
  41. /// <param name="stream">A stream (BsonBinary does not own the stream and will not Dispose it).</param>
  42. /// <param name="settings">A BsonBinaryReaderSettings.</param>
  43. public BsonBinaryReader(Stream stream, BsonBinaryReaderSettings settings)
  44. : base(settings)
  45. {
  46. if (stream == null)
  47. {
  48. throw new ArgumentNullException("stream");
  49. }
  50. if (!stream.CanSeek)
  51. {
  52. throw new ArgumentException("The stream must be capable of seeking.", "stream");
  53. }
  54. _baseStream = stream;
  55. _bsonStream = (stream as BsonStream) ?? new BsonStreamAdapter(stream);
  56. _context = new BsonBinaryReaderContext(null, ContextType.TopLevel, 0, 0);
  57. }
  58. // public properties
  59. /// <summary>
  60. /// Gets the base stream.
  61. /// </summary>
  62. /// <value>
  63. /// The base stream.
  64. /// </value>
  65. public Stream BaseStream
  66. {
  67. get { return _baseStream; }
  68. }
  69. /// <summary>
  70. /// Gets the BSON stream.
  71. /// </summary>
  72. /// <value>
  73. /// The BSON stream.
  74. /// </value>
  75. public BsonStream BsonStream
  76. {
  77. get { return _bsonStream; }
  78. }
  79. /// <summary>
  80. /// Gets the settings of the writer.
  81. /// </summary>
  82. public new BsonBinaryReaderSettings Settings
  83. {
  84. get { return (BsonBinaryReaderSettings)base.Settings; }
  85. }
  86. // public methods
  87. /// <summary>
  88. /// Closes the reader.
  89. /// </summary>
  90. public override void Close()
  91. {
  92. // Close can be called on Disposed objects
  93. State = BsonReaderState.Closed;
  94. }
  95. /// <summary>
  96. /// Gets a bookmark to the reader's current position and state.
  97. /// </summary>
  98. /// <returns>A bookmark.</returns>
  99. public override BsonReaderBookmark GetBookmark()
  100. {
  101. return new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _bsonStream.Position);
  102. }
  103. /// <summary>
  104. /// Determines whether this reader is at end of file.
  105. /// </summary>
  106. /// <returns>
  107. /// Whether this reader is at end of file.
  108. /// </returns>
  109. public override bool IsAtEndOfFile()
  110. {
  111. return _bsonStream.Position >= _bsonStream.Length;
  112. }
  113. /// <summary>
  114. /// Reads BSON binary data from the reader.
  115. /// </summary>
  116. /// <returns>A BsonBinaryData.</returns>
  117. #pragma warning disable 618 // about obsolete BsonBinarySubType.OldBinary
  118. public override BsonBinaryData ReadBinaryData()
  119. {
  120. if (Disposed) { ThrowObjectDisposedException(); }
  121. VerifyBsonType("ReadBinaryData", BsonType.Binary);
  122. int size = ReadSize();
  123. var subType = _bsonStream.ReadBinarySubType();
  124. if (subType == BsonBinarySubType.OldBinary)
  125. {
  126. // sub type OldBinary has two sizes (for historical reasons)
  127. int size2 = ReadSize();
  128. if (size2 != size - 4)
  129. {
  130. throw new FormatException("Binary sub type OldBinary has inconsistent sizes");
  131. }
  132. size = size2;
  133. if (Settings.FixOldBinarySubTypeOnInput)
  134. {
  135. subType = BsonBinarySubType.Binary; // replace obsolete OldBinary with new Binary sub type
  136. }
  137. }
  138. var bytes = _bsonStream.ReadBytes(size);
  139. BsonBinaryData binaryData;
  140. if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
  141. {
  142. GuidRepresentation guidRepresentation;
  143. switch (subType)
  144. {
  145. case BsonBinarySubType.UuidLegacy: guidRepresentation = Settings.GuidRepresentation; break;
  146. case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
  147. default: guidRepresentation = GuidRepresentation.Unspecified; break;
  148. }
  149. binaryData = new BsonBinaryData(bytes, subType, guidRepresentation);
  150. }
  151. else
  152. {
  153. binaryData = new BsonBinaryData(bytes, subType);
  154. }
  155. State = GetNextState();
  156. return binaryData;
  157. }
  158. #pragma warning restore 618
  159. /// <summary>
  160. /// Reads a BSON boolean from the reader.
  161. /// </summary>
  162. /// <returns>A Boolean.</returns>
  163. public override bool ReadBoolean()
  164. {
  165. if (Disposed) { ThrowObjectDisposedException(); }
  166. VerifyBsonType("ReadBoolean", BsonType.Boolean);
  167. State = GetNextState();
  168. return _bsonStream.ReadBoolean();
  169. }
  170. /// <summary>
  171. /// Reads a BsonType from the reader.
  172. /// </summary>
  173. /// <returns>A BsonType.</returns>
  174. public override BsonType ReadBsonType()
  175. {
  176. if (Disposed) { ThrowObjectDisposedException(); }
  177. if (State == BsonReaderState.Initial || State == BsonReaderState.ScopeDocument)
  178. {
  179. // there is an implied type of Document for the top level and for scope documents
  180. CurrentBsonType = BsonType.Document;
  181. State = BsonReaderState.Value;
  182. return CurrentBsonType;
  183. }
  184. if (State != BsonReaderState.Type)
  185. {
  186. ThrowInvalidState("ReadBsonType", BsonReaderState.Type);
  187. }
  188. if (_context.ContextType == ContextType.Array)
  189. {
  190. _context.CurrentArrayIndex++;
  191. }
  192. try
  193. {
  194. CurrentBsonType = _bsonStream.ReadBsonType();
  195. }
  196. catch (FormatException ex)
  197. {
  198. if (ex.Message.StartsWith("Detected unknown BSON type", StringComparison.Ordinal))
  199. {
  200. // insert the element name into the error message
  201. var periodIndex = ex.Message.IndexOf('.');
  202. var dottedElementName = GenerateDottedElementName();
  203. var message = ex.Message.Substring(0, periodIndex) + $" for fieldname \"{dottedElementName}\"" + ex.Message.Substring(periodIndex);
  204. throw new FormatException(message);
  205. }
  206. throw;
  207. }
  208. if (CurrentBsonType == BsonType.EndOfDocument)
  209. {
  210. switch (_context.ContextType)
  211. {
  212. case ContextType.Array:
  213. State = BsonReaderState.EndOfArray;
  214. return BsonType.EndOfDocument;
  215. case ContextType.Document:
  216. case ContextType.ScopeDocument:
  217. State = BsonReaderState.EndOfDocument;
  218. return BsonType.EndOfDocument;
  219. default:
  220. var message = string.Format("BsonType EndOfDocument is not valid when ContextType is {0}.", _context.ContextType);
  221. throw new FormatException(message);
  222. }
  223. }
  224. else
  225. {
  226. switch (_context.ContextType)
  227. {
  228. case ContextType.Array:
  229. _bsonStream.SkipCString(); // ignore array element names
  230. State = BsonReaderState.Value;
  231. break;
  232. case ContextType.Document:
  233. case ContextType.ScopeDocument:
  234. State = BsonReaderState.Name;
  235. break;
  236. default:
  237. throw new BsonInternalException("Unexpected ContextType.");
  238. }
  239. return CurrentBsonType;
  240. }
  241. }
  242. /// <summary>
  243. /// Reads BSON binary data from the reader.
  244. /// </summary>
  245. /// <returns>A byte array.</returns>
  246. #pragma warning disable 618 // about obsolete BsonBinarySubType.OldBinary
  247. public override byte[] ReadBytes()
  248. {
  249. if (Disposed) { ThrowObjectDisposedException(); }
  250. VerifyBsonType("ReadBytes", BsonType.Binary);
  251. int size = ReadSize();
  252. var subType = _bsonStream.ReadBinarySubType();
  253. if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary)
  254. {
  255. var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {0}.", subType);
  256. throw new FormatException(message);
  257. }
  258. State = GetNextState();
  259. return _bsonStream.ReadBytes(size);
  260. }
  261. #pragma warning restore 618
  262. /// <summary>
  263. /// Reads a BSON DateTime from the reader.
  264. /// </summary>
  265. /// <returns>The number of milliseconds since the Unix epoch.</returns>
  266. public override long ReadDateTime()
  267. {
  268. if (Disposed) { ThrowObjectDisposedException(); }
  269. VerifyBsonType("ReadDateTime", BsonType.DateTime);
  270. State = GetNextState();
  271. var value = _bsonStream.ReadInt64();
  272. if (value == BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch + 1)
  273. {
  274. if (Settings.FixOldDateTimeMaxValueOnInput)
  275. {
  276. value = BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch;
  277. }
  278. }
  279. return value;
  280. }
  281. /// <inheritdoc />
  282. public override Decimal128 ReadDecimal128()
  283. {
  284. if (Disposed) { ThrowObjectDisposedException(); }
  285. VerifyBsonType(nameof(ReadDecimal128), BsonType.Decimal128);
  286. State = GetNextState();
  287. return _bsonStream.ReadDecimal128();
  288. }
  289. /// <summary>
  290. /// Reads a BSON Double from the reader.
  291. /// </summary>
  292. /// <returns>A Double.</returns>
  293. public override double ReadDouble()
  294. {
  295. if (Disposed) { ThrowObjectDisposedException(); }
  296. VerifyBsonType("ReadDouble", BsonType.Double);
  297. State = GetNextState();
  298. return _bsonStream.ReadDouble();
  299. }
  300. /// <summary>
  301. /// Reads the end of a BSON array from the reader.
  302. /// </summary>
  303. public override void ReadEndArray()
  304. {
  305. if (Disposed) { ThrowObjectDisposedException(); }
  306. if (_context.ContextType != ContextType.Array)
  307. {
  308. ThrowInvalidContextType("ReadEndArray", _context.ContextType, ContextType.Array);
  309. }
  310. if (State == BsonReaderState.Type)
  311. {
  312. ReadBsonType(); // will set state to EndOfArray if at end of array
  313. }
  314. if (State != BsonReaderState.EndOfArray)
  315. {
  316. ThrowInvalidState("ReadEndArray", BsonReaderState.EndOfArray);
  317. }
  318. _context = _context.PopContext(_bsonStream.Position);
  319. switch (_context.ContextType)
  320. {
  321. case ContextType.Array: State = BsonReaderState.Type; break;
  322. case ContextType.Document: State = BsonReaderState.Type; break;
  323. case ContextType.TopLevel: State = BsonReaderState.Initial; break;
  324. default: throw new BsonInternalException("Unexpected ContextType.");
  325. }
  326. }
  327. /// <summary>
  328. /// Reads the end of a BSON document from the reader.
  329. /// </summary>
  330. public override void ReadEndDocument()
  331. {
  332. if (Disposed) { ThrowObjectDisposedException(); }
  333. if (_context.ContextType != ContextType.Document && _context.ContextType != ContextType.ScopeDocument)
  334. {
  335. ThrowInvalidContextType("ReadEndDocument", _context.ContextType, ContextType.Document, ContextType.ScopeDocument);
  336. }
  337. if (State == BsonReaderState.Type)
  338. {
  339. ReadBsonType(); // will set state to EndOfDocument if at end of document
  340. }
  341. if (State != BsonReaderState.EndOfDocument)
  342. {
  343. ThrowInvalidState("ReadEndDocument", BsonReaderState.EndOfDocument);
  344. }
  345. _context = _context.PopContext(_bsonStream.Position);
  346. if (_context.ContextType == ContextType.JavaScriptWithScope)
  347. {
  348. _context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope
  349. }
  350. switch (_context.ContextType)
  351. {
  352. case ContextType.Array: State = BsonReaderState.Type; break;
  353. case ContextType.Document: State = BsonReaderState.Type; break;
  354. case ContextType.TopLevel: State = BsonReaderState.Initial; break;
  355. default: throw new BsonInternalException("Unexpected ContextType.");
  356. }
  357. }
  358. /// <summary>
  359. /// Reads a BSON Int32 from the reader.
  360. /// </summary>
  361. /// <returns>An Int32.</returns>
  362. public override int ReadInt32()
  363. {
  364. if (Disposed) { ThrowObjectDisposedException(); }
  365. VerifyBsonType("ReadInt32", BsonType.Int32);
  366. State = GetNextState();
  367. return _bsonStream.ReadInt32();
  368. }
  369. /// <summary>
  370. /// Reads a BSON Int64 from the reader.
  371. /// </summary>
  372. /// <returns>An Int64.</returns>
  373. public override long ReadInt64()
  374. {
  375. if (Disposed) { ThrowObjectDisposedException(); }
  376. VerifyBsonType("ReadInt64", BsonType.Int64);
  377. State = GetNextState();
  378. return _bsonStream.ReadInt64();
  379. }
  380. /// <summary>
  381. /// Reads a BSON JavaScript from the reader.
  382. /// </summary>
  383. /// <returns>A string.</returns>
  384. public override string ReadJavaScript()
  385. {
  386. if (Disposed) { ThrowObjectDisposedException(); }
  387. VerifyBsonType("ReadJavaScript", BsonType.JavaScript);
  388. State = GetNextState();
  389. return _bsonStream.ReadString(Settings.Encoding);
  390. }
  391. /// <summary>
  392. /// Reads a BSON JavaScript with scope from the reader (call ReadStartDocument next to read the scope).
  393. /// </summary>
  394. /// <returns>A string.</returns>
  395. public override string ReadJavaScriptWithScope()
  396. {
  397. if (Disposed) { ThrowObjectDisposedException(); }
  398. VerifyBsonType("ReadJavaScriptWithScope", BsonType.JavaScriptWithScope);
  399. var startPosition = _bsonStream.Position; // position of size field
  400. var size = ReadSize();
  401. _context = new BsonBinaryReaderContext(_context, ContextType.JavaScriptWithScope, startPosition, size);
  402. var code = _bsonStream.ReadString(Settings.Encoding);
  403. State = BsonReaderState.ScopeDocument;
  404. return code;
  405. }
  406. /// <summary>
  407. /// Reads a BSON MaxKey from the reader.
  408. /// </summary>
  409. public override void ReadMaxKey()
  410. {
  411. if (Disposed) { ThrowObjectDisposedException(); }
  412. VerifyBsonType("ReadMaxKey", BsonType.MaxKey);
  413. State = GetNextState();
  414. }
  415. /// <summary>
  416. /// Reads a BSON MinKey from the reader.
  417. /// </summary>
  418. public override void ReadMinKey()
  419. {
  420. if (Disposed) { ThrowObjectDisposedException(); }
  421. VerifyBsonType("ReadMinKey", BsonType.MinKey);
  422. State = GetNextState();
  423. }
  424. /// <summary>
  425. /// Reads the name of an element from the reader.
  426. /// </summary>
  427. /// <param name="nameDecoder">The name decoder.</param>
  428. /// <returns>The name of the element.</returns>
  429. public override string ReadName(INameDecoder nameDecoder)
  430. {
  431. if (nameDecoder == null)
  432. {
  433. throw new ArgumentNullException("nameDecoder");
  434. }
  435. if (Disposed) { ThrowObjectDisposedException(); }
  436. if (State == BsonReaderState.Type)
  437. {
  438. ReadBsonType();
  439. }
  440. if (State != BsonReaderState.Name)
  441. {
  442. ThrowInvalidState("ReadName", BsonReaderState.Name);
  443. }
  444. CurrentName = nameDecoder.Decode(_bsonStream, Settings.Encoding);
  445. State = BsonReaderState.Value;
  446. if (_context.ContextType == ContextType.Document)
  447. {
  448. _context.CurrentElementName = CurrentName;
  449. }
  450. return CurrentName;
  451. }
  452. /// <summary>
  453. /// Reads a BSON null from the reader.
  454. /// </summary>
  455. public override void ReadNull()
  456. {
  457. if (Disposed) { ThrowObjectDisposedException(); }
  458. VerifyBsonType("ReadNull", BsonType.Null);
  459. State = GetNextState();
  460. }
  461. /// <summary>
  462. /// Reads a BSON ObjectId from the reader.
  463. /// </summary>
  464. /// <returns>An ObjectId.</returns>
  465. public override ObjectId ReadObjectId()
  466. {
  467. if (Disposed) { ThrowObjectDisposedException(); }
  468. VerifyBsonType("ReadObjectId", BsonType.ObjectId);
  469. State = GetNextState();
  470. return _bsonStream.ReadObjectId();
  471. }
  472. /// <summary>
  473. /// Reads a raw BSON array.
  474. /// </summary>
  475. /// <returns>
  476. /// The raw BSON array.
  477. /// </returns>
  478. public override IByteBuffer ReadRawBsonArray()
  479. {
  480. if (Disposed) { ThrowObjectDisposedException(); }
  481. VerifyBsonType("ReadRawBsonArray", BsonType.Array);
  482. var slice = _bsonStream.ReadSlice();
  483. switch (_context.ContextType)
  484. {
  485. case ContextType.Array: State = BsonReaderState.Type; break;
  486. case ContextType.Document: State = BsonReaderState.Type; break;
  487. case ContextType.TopLevel: State = BsonReaderState.Initial; break;
  488. default: throw new BsonInternalException("Unexpected ContextType.");
  489. }
  490. return slice;
  491. }
  492. /// <summary>
  493. /// Reads a raw BSON document.
  494. /// </summary>
  495. /// <returns>
  496. /// The raw BSON document.
  497. /// </returns>
  498. public override IByteBuffer ReadRawBsonDocument()
  499. {
  500. if (Disposed) { ThrowObjectDisposedException(); }
  501. VerifyBsonType("ReadRawBsonDocument", BsonType.Document);
  502. var slice = _bsonStream.ReadSlice();
  503. if (_context.ContextType == ContextType.JavaScriptWithScope)
  504. {
  505. _context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope
  506. }
  507. switch (_context.ContextType)
  508. {
  509. case ContextType.Array: State = BsonReaderState.Type; break;
  510. case ContextType.Document: State = BsonReaderState.Type; break;
  511. case ContextType.TopLevel: State = BsonReaderState.Initial; break;
  512. default: throw new BsonInternalException("Unexpected ContextType.");
  513. }
  514. return slice;
  515. }
  516. /// <summary>
  517. /// Reads a BSON regular expression from the reader.
  518. /// </summary>
  519. /// <returns>A BsonRegularExpression.</returns>
  520. public override BsonRegularExpression ReadRegularExpression()
  521. {
  522. if (Disposed) { ThrowObjectDisposedException(); }
  523. VerifyBsonType("ReadRegularExpression", BsonType.RegularExpression);
  524. State = GetNextState();
  525. var pattern = _bsonStream.ReadCString(Settings.Encoding);
  526. var options = _bsonStream.ReadCString(Settings.Encoding);
  527. return new BsonRegularExpression(pattern, options);
  528. }
  529. /// <summary>
  530. /// Reads the start of a BSON array.
  531. /// </summary>
  532. public override void ReadStartArray()
  533. {
  534. if (Disposed) { ThrowObjectDisposedException(); }
  535. VerifyBsonType("ReadStartArray", BsonType.Array);
  536. var startPosition = _bsonStream.Position; // position of size field
  537. var size = ReadSize();
  538. _context = new BsonBinaryReaderContext(_context, ContextType.Array, startPosition, size);
  539. State = BsonReaderState.Type;
  540. }
  541. /// <summary>
  542. /// Reads the start of a BSON document.
  543. /// </summary>
  544. public override void ReadStartDocument()
  545. {
  546. if (Disposed) { ThrowObjectDisposedException(); }
  547. VerifyBsonType("ReadStartDocument", BsonType.Document);
  548. var contextType = (State == BsonReaderState.ScopeDocument) ? ContextType.ScopeDocument : ContextType.Document;
  549. var startPosition = _bsonStream.Position; // position of size field
  550. var size = ReadSize();
  551. _context = new BsonBinaryReaderContext(_context, contextType, startPosition, size);
  552. State = BsonReaderState.Type;
  553. }
  554. /// <summary>
  555. /// Reads a BSON string from the reader.
  556. /// </summary>
  557. /// <returns>A String.</returns>
  558. public override string ReadString()
  559. {
  560. if (Disposed) { ThrowObjectDisposedException(); }
  561. VerifyBsonType("ReadString", BsonType.String);
  562. State = GetNextState();
  563. return _bsonStream.ReadString(Settings.Encoding);
  564. }
  565. /// <summary>
  566. /// Reads a BSON symbol from the reader.
  567. /// </summary>
  568. /// <returns>A string.</returns>
  569. public override string ReadSymbol()
  570. {
  571. if (Disposed) { ThrowObjectDisposedException(); }
  572. VerifyBsonType("ReadSymbol", BsonType.Symbol);
  573. State = GetNextState();
  574. return _bsonStream.ReadString(Settings.Encoding);
  575. }
  576. /// <summary>
  577. /// Reads a BSON timestamp from the reader.
  578. /// </summary>
  579. /// <returns>The combined timestamp/increment.</returns>
  580. public override long ReadTimestamp()
  581. {
  582. if (Disposed) { ThrowObjectDisposedException(); }
  583. VerifyBsonType("ReadTimestamp", BsonType.Timestamp);
  584. State = GetNextState();
  585. return _bsonStream.ReadInt64();
  586. }
  587. /// <summary>
  588. /// Reads a BSON undefined from the reader.
  589. /// </summary>
  590. public override void ReadUndefined()
  591. {
  592. if (Disposed) { ThrowObjectDisposedException(); }
  593. VerifyBsonType("ReadUndefined", BsonType.Undefined);
  594. State = GetNextState();
  595. }
  596. /// <summary>
  597. /// Returns the reader to previously bookmarked position and state.
  598. /// </summary>
  599. /// <param name="bookmark">The bookmark.</param>
  600. public override void ReturnToBookmark(BsonReaderBookmark bookmark)
  601. {
  602. var binaryReaderBookmark = (BsonBinaryReaderBookmark)bookmark;
  603. State = binaryReaderBookmark.State;
  604. CurrentBsonType = binaryReaderBookmark.CurrentBsonType;
  605. CurrentName = binaryReaderBookmark.CurrentName;
  606. _context = binaryReaderBookmark.CloneContext();
  607. _bsonStream.Position = binaryReaderBookmark.Position;
  608. }
  609. /// <summary>
  610. /// Skips the name (reader must be positioned on a name).
  611. /// </summary>
  612. public override void SkipName()
  613. {
  614. if (Disposed) { ThrowObjectDisposedException(); }
  615. if (State != BsonReaderState.Name)
  616. {
  617. ThrowInvalidState("SkipName", BsonReaderState.Name);
  618. }
  619. _bsonStream.SkipCString();
  620. CurrentName = null;
  621. State = BsonReaderState.Value;
  622. if (_context.ContextType == ContextType.Document)
  623. {
  624. _context.CurrentElementName = CurrentName;
  625. }
  626. }
  627. /// <summary>
  628. /// Skips the value (reader must be positioned on a value).
  629. /// </summary>
  630. public override void SkipValue()
  631. {
  632. if (Disposed) { ThrowObjectDisposedException(); }
  633. if (State != BsonReaderState.Value)
  634. {
  635. ThrowInvalidState("SkipValue", BsonReaderState.Value);
  636. }
  637. int skip;
  638. switch (CurrentBsonType)
  639. {
  640. case BsonType.Array: skip = ReadSize() - 4; break;
  641. case BsonType.Binary: skip = ReadSize() + 1; break;
  642. case BsonType.Boolean: skip = 1; break;
  643. case BsonType.DateTime: skip = 8; break;
  644. case BsonType.Document: skip = ReadSize() - 4; break;
  645. case BsonType.Decimal128: skip = 16; break;
  646. case BsonType.Double: skip = 8; break;
  647. case BsonType.Int32: skip = 4; break;
  648. case BsonType.Int64: skip = 8; break;
  649. case BsonType.JavaScript: skip = ReadSize(); break;
  650. case BsonType.JavaScriptWithScope: skip = ReadSize() - 4; break;
  651. case BsonType.MaxKey: skip = 0; break;
  652. case BsonType.MinKey: skip = 0; break;
  653. case BsonType.Null: skip = 0; break;
  654. case BsonType.ObjectId: skip = 12; break;
  655. case BsonType.RegularExpression: _bsonStream.SkipCString(); _bsonStream.SkipCString(); skip = 0; break;
  656. case BsonType.String: skip = ReadSize(); break;
  657. case BsonType.Symbol: skip = ReadSize(); break;
  658. case BsonType.Timestamp: skip = 8; break;
  659. case BsonType.Undefined: skip = 0; break;
  660. default: throw new BsonInternalException("Unexpected BsonType.");
  661. }
  662. _bsonStream.Seek(skip, SeekOrigin.Current);
  663. State = BsonReaderState.Type;
  664. }
  665. // protected methods
  666. /// <summary>
  667. /// Disposes of any resources used by the reader.
  668. /// </summary>
  669. /// <param name="disposing">True if called from Dispose.</param>
  670. protected override void Dispose(bool disposing)
  671. {
  672. // don't Dispose the _stream because we don't own it
  673. if (disposing)
  674. {
  675. try
  676. {
  677. Close();
  678. }
  679. catch { } // ignore exceptions
  680. }
  681. base.Dispose(disposing);
  682. }
  683. // private methods
  684. private string GenerateDottedElementName()
  685. {
  686. string elementName;
  687. if (_context.ContextType == ContextType.Document)
  688. {
  689. try
  690. {
  691. elementName = _bsonStream.ReadCString(Utf8Encodings.Lenient);
  692. }
  693. catch
  694. {
  695. elementName = "?"; // ignore exception
  696. }
  697. }
  698. else if (_context.ContextType == ContextType.Array)
  699. {
  700. elementName = _context.CurrentArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
  701. }
  702. else
  703. {
  704. elementName = "?";
  705. }
  706. return GenerateDottedElementName(_context.ParentContext, elementName);
  707. }
  708. private string GenerateDottedElementName(BsonBinaryReaderContext context, string elementName)
  709. {
  710. if (context.ContextType == ContextType.Document)
  711. {
  712. return GenerateDottedElementName(context.ParentContext, (context.CurrentElementName ?? "?") + "." + elementName);
  713. }
  714. else if (context.ContextType == ContextType.Array)
  715. {
  716. var indexElementName = context.CurrentArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
  717. return GenerateDottedElementName(context.ParentContext, indexElementName + "." + elementName);
  718. }
  719. else if (context.ParentContext != null)
  720. {
  721. return GenerateDottedElementName(context.ParentContext, "?." + elementName);
  722. }
  723. else
  724. {
  725. return elementName;
  726. }
  727. }
  728. private BsonReaderState GetNextState()
  729. {
  730. switch (_context.ContextType)
  731. {
  732. case ContextType.Array:
  733. case ContextType.Document:
  734. case ContextType.ScopeDocument:
  735. return BsonReaderState.Type;
  736. case ContextType.TopLevel:
  737. return BsonReaderState.Initial;
  738. default:
  739. throw new BsonInternalException("Unexpected ContextType.");
  740. }
  741. }
  742. private int ReadSize()
  743. {
  744. int size = _bsonStream.ReadInt32();
  745. if (size < 0)
  746. {
  747. var message = string.Format("Size {0} is not valid because it is negative.", size);
  748. throw new FormatException(message);
  749. }
  750. if (size > Settings.MaxDocumentSize)
  751. {
  752. var message = string.Format("Size {0} is not valid because it is larger than MaxDocumentSize {1}.", size, Settings.MaxDocumentSize);
  753. throw new FormatException(message);
  754. }
  755. return size;
  756. }
  757. }
  758. }