BsonBuffer.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /* Copyright 2010-2014 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.IO;
  17. using System.Text;
  18. namespace MongoDB.Bson.IO
  19. {
  20. /// <summary>
  21. /// Represents a buffer for BSON encoded bytes.
  22. /// </summary>
  23. public class BsonBuffer : IDisposable
  24. {
  25. // private static fields
  26. private static readonly string[] __asciiStringTable = BuildAsciiStringTable();
  27. private static readonly bool[] __validBsonTypes = new bool[256];
  28. // private fields
  29. private bool _disposed = false;
  30. private IByteBuffer _byteBuffer;
  31. private bool _disposeByteBuffer;
  32. // static constructor
  33. static BsonBuffer()
  34. {
  35. foreach (BsonType bsonType in Enum.GetValues(typeof(BsonType)))
  36. {
  37. __validBsonTypes[(byte)bsonType] = true;
  38. }
  39. }
  40. // constructors
  41. /// <summary>
  42. /// Initializes a new instance of the BsonBuffer class.
  43. /// </summary>
  44. public BsonBuffer()
  45. : this(new MultiChunkBuffer(BsonChunkPool.Default), true)
  46. {
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of the <see cref="BsonBuffer" /> class.
  50. /// </summary>
  51. /// <param name="byteBuffer">The buffer.</param>
  52. /// <param name="disposeByteBuffer">if set to <c>true</c> this BsonBuffer will own the byte buffer and when Dispose is called the byte buffer will be Disposed also.</param>
  53. public BsonBuffer(IByteBuffer byteBuffer, bool disposeByteBuffer)
  54. {
  55. _byteBuffer = byteBuffer;
  56. _disposeByteBuffer = disposeByteBuffer;
  57. }
  58. // public properties
  59. /// <summary>
  60. /// Gets the byte buffer.
  61. /// </summary>
  62. /// <value>
  63. /// The byte buffer.
  64. /// </value>
  65. public IByteBuffer ByteBuffer
  66. {
  67. get { return _byteBuffer; }
  68. }
  69. /// <summary>
  70. /// Gets or sets the length of the data in the buffer.
  71. /// </summary>
  72. public int Length
  73. {
  74. get
  75. {
  76. ThrowIfDisposed();
  77. return _byteBuffer.Length;
  78. }
  79. set
  80. {
  81. ThrowIfDisposed();
  82. _byteBuffer.Length = value;
  83. }
  84. }
  85. /// <summary>
  86. /// Gets or sets the current position in the buffer.
  87. /// </summary>
  88. public int Position
  89. {
  90. get
  91. {
  92. ThrowIfDisposed();
  93. return _byteBuffer.Position;
  94. }
  95. set
  96. {
  97. ThrowIfDisposed();
  98. _byteBuffer.Position = value;
  99. }
  100. }
  101. // private static methods
  102. private static string[] BuildAsciiStringTable()
  103. {
  104. var asciiStringTable = new string[128];
  105. for (int i = 0; i < 128; ++i)
  106. {
  107. asciiStringTable[i] = new string((char)i, 1);
  108. }
  109. return asciiStringTable;
  110. }
  111. // public methods
  112. /// <summary>
  113. /// Backpatches the length of an object.
  114. /// </summary>
  115. /// <param name="position">The start position of the object.</param>
  116. /// <param name="length">The length of the object.</param>
  117. public void Backpatch(int position, int length)
  118. {
  119. ThrowIfDisposed();
  120. var savedPosition = _byteBuffer.Position;
  121. _byteBuffer.Position = position;
  122. WriteInt32(length);
  123. _byteBuffer.Position = savedPosition;
  124. }
  125. /// <summary>
  126. /// Clears the data in the buffer.
  127. /// </summary>
  128. public void Clear()
  129. {
  130. ThrowIfDisposed();
  131. _byteBuffer.Clear();
  132. }
  133. /// <summary>
  134. /// Copies data from the buffer to a byte array.
  135. /// </summary>
  136. /// <param name="sourceOffset">The source offset in the buffer.</param>
  137. /// <param name="destination">The destination byte array.</param>
  138. /// <param name="destinationOffset">The destination offset in the byte array.</param>
  139. /// <param name="count">The number of bytes to copy.</param>
  140. [Obsolete("Use ReadBytes instead.")]
  141. public void CopyTo(int sourceOffset, byte[] destination, int destinationOffset, int count)
  142. {
  143. ThrowIfDisposed();
  144. var savedPosition = _byteBuffer.Position;
  145. _byteBuffer.Position = sourceOffset;
  146. _byteBuffer.ReadBytes(destination, destinationOffset, count);
  147. _byteBuffer.Position = savedPosition;
  148. }
  149. /// <summary>
  150. /// Disposes of any resources held by the buffer.
  151. /// </summary>
  152. public void Dispose()
  153. {
  154. Dispose(true);
  155. GC.SuppressFinalize(this);
  156. }
  157. /// <summary>
  158. /// Loads the buffer from a Stream (the Stream must be positioned at a 4 byte length field).
  159. /// </summary>
  160. /// <param name="stream">The Stream.</param>
  161. public void LoadFrom(Stream stream)
  162. {
  163. LoadFrom(stream, 4); // does not advance position
  164. int length = ReadInt32(); // advances position 4 bytes
  165. LoadFrom(stream, length - 4); // does not advance position
  166. Position -= 4; // move back to just before the length field
  167. }
  168. /// <summary>
  169. /// Loads the buffer from a Stream (leaving the position in the buffer unchanged).
  170. /// </summary>
  171. /// <param name="stream">The stream.</param>
  172. /// <param name="count">The number of bytes to load.</param>
  173. public void LoadFrom(Stream stream, int count)
  174. {
  175. ThrowIfDisposed();
  176. _byteBuffer.LoadFrom(stream, count); // does not advance position
  177. }
  178. /// <summary>
  179. /// Peeks at the next byte in the buffer and returns it as a BsonType.
  180. /// </summary>
  181. /// <returns>A BsonType.</returns>
  182. [Obsolete("Use ReadBsonType instead.")]
  183. public BsonType PeekBsonType()
  184. {
  185. ThrowIfDisposed();
  186. var value = ReadBsonType();
  187. Position -= 1;
  188. return value;
  189. }
  190. /// <summary>
  191. /// Peeks at the next byte in the buffer.
  192. /// </summary>
  193. /// <returns>A Byte.</returns>
  194. [Obsolete("Use ReadByte instead.")]
  195. public byte PeekByte()
  196. {
  197. ThrowIfDisposed();
  198. var value = ReadByte();
  199. Position -= 1;
  200. return value;
  201. }
  202. /// <summary>
  203. /// Reads a BSON Boolean from the buffer.
  204. /// </summary>
  205. /// <returns>A Boolean.</returns>
  206. public bool ReadBoolean()
  207. {
  208. ThrowIfDisposed();
  209. return _byteBuffer.ReadByte() != 0;
  210. }
  211. /// <summary>
  212. /// Reads a BSON type from the buffer.
  213. /// </summary>
  214. /// <returns>A BsonType.</returns>
  215. public BsonType ReadBsonType()
  216. {
  217. ThrowIfDisposed();
  218. var bsonType = (int)_byteBuffer.ReadByte();
  219. if (!__validBsonTypes[bsonType])
  220. {
  221. string message = string.Format("Invalid BsonType {0}.", bsonType);
  222. throw new Exception(message);
  223. }
  224. return (BsonType)bsonType;
  225. }
  226. /// <summary>
  227. /// Reads a byte from the buffer.
  228. /// </summary>
  229. /// <returns>A Byte.</returns>
  230. public byte ReadByte()
  231. {
  232. ThrowIfDisposed();
  233. return _byteBuffer.ReadByte();
  234. }
  235. /// <summary>
  236. /// Reads bytes from the buffer.
  237. /// </summary>
  238. /// <param name="count">The number of bytes to read.</param>
  239. /// <returns>A byte array.</returns>
  240. public byte[] ReadBytes(int count)
  241. {
  242. ThrowIfDisposed();
  243. return _byteBuffer.ReadBytes(count);
  244. }
  245. /// <summary>
  246. /// Reads a BSON Double from the buffer.
  247. /// </summary>
  248. /// <returns>A Double.</returns>
  249. public double ReadDouble()
  250. {
  251. ThrowIfDisposed();
  252. var segment = _byteBuffer.ReadBackingBytes(8);
  253. if (segment.Count >= 8)
  254. {
  255. return BitConverter.ToDouble(segment.Array, segment.Offset);
  256. }
  257. else
  258. {
  259. var bytes = _byteBuffer.ReadBytes(8);
  260. return BitConverter.ToDouble(bytes, 0);
  261. }
  262. }
  263. /// <summary>
  264. /// Reads a BSON Int32 from the reader.
  265. /// </summary>
  266. /// <returns>An Int32.</returns>
  267. public int ReadInt32()
  268. {
  269. ThrowIfDisposed();
  270. var segment = _byteBuffer.ReadBackingBytes(4);
  271. if (segment.Count >= 4)
  272. {
  273. // for int only we come out ahead with this code vs using BitConverter
  274. return
  275. ((int)segment.Array[segment.Offset + 0]) +
  276. ((int)segment.Array[segment.Offset + 1] << 8) +
  277. ((int)segment.Array[segment.Offset + 2] << 16) +
  278. ((int)segment.Array[segment.Offset + 3] << 24);
  279. }
  280. else
  281. {
  282. var bytes = _byteBuffer.ReadBytes(4);
  283. return BitConverter.ToInt32(bytes, 0);
  284. }
  285. }
  286. /// <summary>
  287. /// Reads a BSON Int64 from the reader.
  288. /// </summary>
  289. /// <returns>An Int64.</returns>
  290. public long ReadInt64()
  291. {
  292. ThrowIfDisposed();
  293. var segment = _byteBuffer.ReadBackingBytes(8);
  294. if (segment.Count >= 8)
  295. {
  296. return BitConverter.ToInt64(segment.Array, segment.Offset);
  297. }
  298. else
  299. {
  300. var bytes = _byteBuffer.ReadBytes(8);
  301. return BitConverter.ToInt64(bytes, 0);
  302. }
  303. }
  304. /// <summary>
  305. /// Reads a BSON ObjectId from the reader.
  306. /// </summary>
  307. /// <returns>An ObjectId.</returns>
  308. public ObjectId ReadObjectId()
  309. {
  310. ThrowIfDisposed();
  311. var segment = _byteBuffer.ReadBackingBytes(12);
  312. if (segment.Count >= 12)
  313. {
  314. var bytes = segment.Array;
  315. var offset = segment.Offset;
  316. var timestamp = (bytes[offset + 0] << 24) + (bytes[offset + 1] << 16) + (bytes[offset + 2] << 8) + bytes[offset + 3];
  317. var machine = (bytes[offset + 4] << 16) + (bytes[offset + 5] << 8) + bytes[offset + 6];
  318. var pid = (short)((bytes[offset + 7] << 8) + bytes[offset + 8]);
  319. var increment = (bytes[offset + 9] << 16) + (bytes[offset + 10] << 8) + bytes[offset + 11];
  320. return new ObjectId(timestamp, machine, pid, increment);
  321. }
  322. else
  323. {
  324. var bytes = _byteBuffer.ReadBytes(12);
  325. return new ObjectId(bytes);
  326. }
  327. }
  328. /// <summary>
  329. /// Reads a BSON ObjectId from the reader.
  330. /// </summary>
  331. /// <param name="timestamp">The timestamp.</param>
  332. /// <param name="machine">The machine hash.</param>
  333. /// <param name="pid">The PID.</param>
  334. /// <param name="increment">The increment.</param>
  335. [Obsolete("Use ReadObjectId() instead.")]
  336. public void ReadObjectId(out int timestamp, out int machine, out short pid, out int increment)
  337. {
  338. var objectId = ReadObjectId();
  339. timestamp = objectId.Timestamp;
  340. machine = objectId.Machine;
  341. pid = objectId.Pid;
  342. increment = objectId.Increment;
  343. }
  344. /// <summary>
  345. /// Reads a BSON string from the reader.
  346. /// </summary>
  347. /// <returns>A String.</returns>
  348. public string ReadString(UTF8Encoding encoding)
  349. {
  350. ThrowIfDisposed();
  351. var length = ReadInt32(); // length including the null terminator
  352. if (length <= 0)
  353. {
  354. var message = string.Format("Invalid string length: {0} (the length includes the null terminator so it must be greater than or equal to 1).", length);
  355. throw new Exception(message);
  356. }
  357. string value;
  358. byte finalByte;
  359. var segment = _byteBuffer.ReadBackingBytes(length);
  360. if (segment.Count >= length)
  361. {
  362. value = DecodeUtf8String(encoding, segment.Array, segment.Offset, length - 1);
  363. finalByte = segment.Array[segment.Offset + length - 1];
  364. }
  365. else
  366. {
  367. var bytes = _byteBuffer.ReadBytes(length);
  368. value = DecodeUtf8String(encoding, bytes, 0, length - 1);
  369. finalByte = bytes[length - 1];
  370. }
  371. if (finalByte != 0)
  372. {
  373. throw new Exception("String is missing null terminator.");
  374. }
  375. return value;
  376. }
  377. /// <summary>
  378. /// Reads a BSON CString from the reader (a null terminated string).
  379. /// </summary>
  380. /// <returns>A string.</returns>
  381. public string ReadCString(UTF8Encoding encoding)
  382. {
  383. ThrowIfDisposed();
  384. var nullPosition = _byteBuffer.FindNullByte();
  385. if (nullPosition == -1)
  386. {
  387. throw new BsonSerializationException("Missing null terminator.");
  388. }
  389. return ReadCString(encoding, nullPosition);
  390. }
  391. /// <summary>
  392. /// Reads an element name.
  393. /// </summary>
  394. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  395. /// <param name="bsonTrie">An optional BsonTrie to use during decoding.</param>
  396. /// <param name="found">Set to true if the string was found in the trie.</param>
  397. /// <param name="value">Set to the value found in the trie; otherwise, null.</param>
  398. /// <returns>A string.</returns>
  399. public string ReadName<TValue>(BsonTrie<TValue> bsonTrie, out bool found, out TValue value)
  400. {
  401. ThrowIfDisposed();
  402. found = false;
  403. value = default(TValue);
  404. if (bsonTrie == null)
  405. {
  406. return ReadCString(new UTF8Encoding(false, true)); // always use strict encoding for names
  407. }
  408. var savedPosition = _byteBuffer.Position;
  409. var bsonTrieNode = bsonTrie.Root;
  410. while (true)
  411. {
  412. var keyByte = _byteBuffer.ReadByte();
  413. if (keyByte == 0)
  414. {
  415. if (bsonTrieNode.HasValue)
  416. {
  417. found = true;
  418. value = bsonTrieNode.Value;
  419. return bsonTrieNode.ElementName;
  420. }
  421. else
  422. {
  423. var nullPosition = _byteBuffer.Position - 1;
  424. _byteBuffer.Position = savedPosition;
  425. return ReadCString(new UTF8Encoding(false, true), nullPosition); // always use strict encoding for names
  426. }
  427. }
  428. bsonTrieNode = bsonTrieNode.GetChild(keyByte);
  429. if (bsonTrieNode == null)
  430. {
  431. var nullPosition = _byteBuffer.FindNullByte(); // starting from where we got so far
  432. _byteBuffer.Position = savedPosition;
  433. return ReadCString(new UTF8Encoding(false, true), nullPosition); // always use strict encoding for names
  434. }
  435. }
  436. }
  437. /// <summary>
  438. /// Skips over bytes in the buffer (advances the position).
  439. /// </summary>
  440. /// <param name="count">The number of bytes to skip.</param>
  441. public void Skip(int count)
  442. {
  443. _byteBuffer.Position += count;
  444. }
  445. /// <summary>
  446. /// Skips over a CString in the buffer (advances the position).
  447. /// </summary>
  448. public void SkipCString()
  449. {
  450. ThrowIfDisposed();
  451. var nullPosition = _byteBuffer.FindNullByte();
  452. if (nullPosition == -1)
  453. {
  454. throw new Exception("String is missing null terminator");
  455. }
  456. _byteBuffer.Position = nullPosition + 1;
  457. }
  458. /// <summary>
  459. /// Converts the buffer to a byte array.
  460. /// </summary>
  461. /// <returns>A byte array.</returns>
  462. public byte[] ToByteArray()
  463. {
  464. ThrowIfDisposed();
  465. var savedPosition = _byteBuffer.Position;
  466. _byteBuffer.Position = 0;
  467. var byteArray = _byteBuffer.ReadBytes(_byteBuffer.Length);
  468. _byteBuffer.Position = savedPosition;
  469. return byteArray;
  470. }
  471. /// <summary>
  472. /// Writes a BSON Boolean to the buffer.
  473. /// </summary>
  474. /// <param name="value">The Boolean value.</param>
  475. public void WriteBoolean(bool value)
  476. {
  477. ThrowIfDisposed();
  478. _byteBuffer.WriteByte(value ? (byte)1 : (byte)0);
  479. }
  480. /// <summary>
  481. /// Writes a byte to the buffer.
  482. /// </summary>
  483. /// <param name="value">A byte.</param>
  484. public void WriteByte(byte value)
  485. {
  486. ThrowIfDisposed();
  487. _byteBuffer.WriteByte(value);
  488. }
  489. /// <summary>
  490. /// Writes bytes to the buffer.
  491. /// </summary>
  492. /// <param name="value">A byte array.</param>
  493. public void WriteBytes(byte[] value)
  494. {
  495. ThrowIfDisposed();
  496. _byteBuffer.WriteBytes(value);
  497. }
  498. /// <summary>
  499. /// Writes a CString to the buffer.
  500. /// </summary>
  501. /// <param name="encoding">A UTF8 encoding.</param>
  502. /// <param name="value">A string.</param>
  503. public void WriteCString(UTF8Encoding encoding, string value)
  504. {
  505. if (value == null)
  506. {
  507. throw new ArgumentNullException("value");
  508. }
  509. if (value.IndexOf('\0') != -1)
  510. {
  511. throw new ArgumentException("CStrings cannot contain nulls.", "value");
  512. }
  513. ThrowIfDisposed();
  514. var maxLength = encoding.GetMaxByteCount(value.Length) + 1;
  515. var segment = _byteBuffer.WriteBackingBytes(maxLength);
  516. if (segment.Count >= maxLength)
  517. {
  518. var length = encoding.GetBytes(value, 0, value.Length, segment.Array, segment.Offset);
  519. segment.Array[segment.Offset + length] = 0;
  520. _byteBuffer.Position += length + 1;
  521. }
  522. else
  523. {
  524. _byteBuffer.WriteBytes(encoding.GetBytes(value));
  525. _byteBuffer.WriteByte(0);
  526. }
  527. }
  528. /// <summary>
  529. /// Writes a BSON Double to the buffer.
  530. /// </summary>
  531. /// <param name="value">The Double value.</param>
  532. public void WriteDouble(double value)
  533. {
  534. ThrowIfDisposed();
  535. _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
  536. }
  537. /// <summary>
  538. /// Writes a BSON Int32 to the buffer.
  539. /// </summary>
  540. /// <param name="value">The Int32 value.</param>
  541. public void WriteInt32(int value)
  542. {
  543. ThrowIfDisposed();
  544. var segment = _byteBuffer.WriteBackingBytes(4);
  545. if (segment.Count >= 4)
  546. {
  547. segment.Array[segment.Offset + 0] = (byte)(value);
  548. segment.Array[segment.Offset + 1] = (byte)(value >> 8);
  549. segment.Array[segment.Offset + 2] = (byte)(value >> 16);
  550. segment.Array[segment.Offset + 3] = (byte)(value >> 24);
  551. _byteBuffer.Position += 4;
  552. }
  553. else
  554. {
  555. _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
  556. }
  557. }
  558. /// <summary>
  559. /// Writes a BSON Int64 to the buffer.
  560. /// </summary>
  561. /// <param name="value">The Int64 value.</param>
  562. public void WriteInt64(long value)
  563. {
  564. ThrowIfDisposed();
  565. _byteBuffer.WriteBytes(BitConverter.GetBytes(value));
  566. }
  567. /// <summary>
  568. /// Writes a BSON ObjectId to the buffer.
  569. /// </summary>
  570. /// <param name="timestamp">The timestamp.</param>
  571. /// <param name="machine">The machine hash.</param>
  572. /// <param name="pid">The PID.</param>
  573. /// <param name="increment">The increment.</param>
  574. [Obsolete("Use WriteObjectId(ObjectId objectId) instead.")]
  575. public void WriteObjectId(int timestamp, int machine, short pid, int increment)
  576. {
  577. var objectId = new ObjectId(timestamp, machine, pid, increment);
  578. WriteObjectId(objectId);
  579. }
  580. /// <summary>
  581. /// Writes a BSON ObjectId to the buffer.
  582. /// </summary>
  583. /// <param name="objectId">The ObjectId.</param>
  584. public void WriteObjectId(ObjectId objectId)
  585. {
  586. ThrowIfDisposed();
  587. var segment = _byteBuffer.WriteBackingBytes(12);
  588. if (segment.Count >= 12)
  589. {
  590. var timestamp = objectId.Timestamp;
  591. var machine = objectId.Machine;
  592. var pid = objectId.Pid;
  593. var increment = objectId.Increment;
  594. segment.Array[segment.Offset + 0] = (byte)(timestamp >> 24);
  595. segment.Array[segment.Offset + 1] = (byte)(timestamp >> 16);
  596. segment.Array[segment.Offset + 2] = (byte)(timestamp >> 8);
  597. segment.Array[segment.Offset + 3] = (byte)(timestamp);
  598. segment.Array[segment.Offset + 4] = (byte)(machine >> 16);
  599. segment.Array[segment.Offset + 5] = (byte)(machine >> 8);
  600. segment.Array[segment.Offset + 6] = (byte)(machine);
  601. segment.Array[segment.Offset + 7] = (byte)(pid >> 8);
  602. segment.Array[segment.Offset + 8] = (byte)(pid);
  603. segment.Array[segment.Offset + 9] = (byte)(increment >> 16);
  604. segment.Array[segment.Offset + 10] = (byte)(increment >> 8);
  605. segment.Array[segment.Offset + 11] = (byte)(increment);
  606. _byteBuffer.Position += 12;
  607. }
  608. else
  609. {
  610. _byteBuffer.WriteBytes(objectId.ToByteArray());
  611. }
  612. }
  613. /// <summary>
  614. /// Writes a BSON String to the buffer.
  615. /// </summary>
  616. /// <param name="encoding">A UTF8 encoding.</param>
  617. /// <param name="value">The String value.</param>
  618. public void WriteString(UTF8Encoding encoding, string value)
  619. {
  620. ThrowIfDisposed();
  621. var maxLength = encoding.GetMaxByteCount(value.Length) + 5;
  622. var segment = _byteBuffer.WriteBackingBytes(maxLength);
  623. if (segment.Count >= maxLength)
  624. {
  625. var length = encoding.GetBytes(value, 0, value.Length, segment.Array, segment.Offset + 4);
  626. var lengthPlusOne = length + 1;
  627. segment.Array[segment.Offset + 0] = (byte)(lengthPlusOne); // now we know the length
  628. segment.Array[segment.Offset + 1] = (byte)(lengthPlusOne >> 8);
  629. segment.Array[segment.Offset + 2] = (byte)(lengthPlusOne >> 16);
  630. segment.Array[segment.Offset + 3] = (byte)(lengthPlusOne >> 24);
  631. segment.Array[segment.Offset + 4 + length] = 0;
  632. _byteBuffer.Position += length + 5;
  633. }
  634. else
  635. {
  636. var bytes = encoding.GetBytes(value);
  637. WriteInt32(bytes.Length + 1);
  638. _byteBuffer.WriteBytes(bytes);
  639. _byteBuffer.WriteByte(0);
  640. }
  641. }
  642. /// <summary>
  643. /// Writes all the data in the buffer to a Stream.
  644. /// </summary>
  645. /// <param name="stream">The Stream.</param>
  646. public void WriteTo(Stream stream)
  647. {
  648. ThrowIfDisposed();
  649. _byteBuffer.WriteTo(stream);
  650. }
  651. /// <summary>
  652. /// Writes a 32-bit zero the the buffer.
  653. /// </summary>
  654. [Obsolete("Use WriteByte or WriteInt32 instead.")]
  655. public void WriteZero()
  656. {
  657. ThrowIfDisposed();
  658. WriteInt32(0);
  659. }
  660. // private static methods
  661. private static string DecodeUtf8String(UTF8Encoding encoding, byte[] buffer, int index, int count)
  662. {
  663. switch (count)
  664. {
  665. // special case empty strings
  666. case 0:
  667. return string.Empty;
  668. // special case single character strings
  669. case 1:
  670. var byte1 = (int)buffer[index];
  671. if (byte1 < __asciiStringTable.Length)
  672. {
  673. return __asciiStringTable[byte1];
  674. }
  675. break;
  676. }
  677. return encoding.GetString(buffer, index, count);
  678. }
  679. // protected methods
  680. /// <summary>
  681. /// Releases unmanaged and - optionally - managed resources.
  682. /// </summary>
  683. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  684. protected virtual void Dispose(bool disposing)
  685. {
  686. if (!_disposed)
  687. {
  688. if (disposing)
  689. {
  690. if (_byteBuffer != null)
  691. {
  692. if (_disposeByteBuffer)
  693. {
  694. _byteBuffer.Dispose();
  695. }
  696. _byteBuffer = null;
  697. }
  698. }
  699. _disposed = true;
  700. }
  701. }
  702. /// <summary>
  703. /// Throws if disposed.
  704. /// </summary>
  705. /// <exception cref="System.ObjectDisposedException"></exception>
  706. protected void ThrowIfDisposed()
  707. {
  708. if (_disposed)
  709. {
  710. throw new ObjectDisposedException(GetType().Name);
  711. }
  712. }
  713. // private methods
  714. private string ReadCString(UTF8Encoding encoding, int nullPosition)
  715. {
  716. if (nullPosition == -1)
  717. {
  718. throw new BsonSerializationException("Missing null terminator.");
  719. }
  720. var length = nullPosition - _byteBuffer.Position + 1;
  721. var segment = _byteBuffer.ReadBackingBytes(length);
  722. if (segment.Count >= length)
  723. {
  724. return DecodeUtf8String(encoding, segment.Array, segment.Offset, length - 1);
  725. }
  726. else
  727. {
  728. var bytes = _byteBuffer.ReadBytes(length);
  729. return DecodeUtf8String(encoding, bytes, 0, length - 1);
  730. }
  731. }
  732. }
  733. }