BsonBinaryReader.cs 31 KB

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