CodedOutputStream.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2008 Google Inc. All rights reserved.
  4. // https://developers.google.com/protocol-buffers/
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. #endregion
  32. using System;
  33. using System.IO;
  34. using System.Text;
  35. namespace XGame.Framework.Network.Protobuf
  36. {
  37. /// <summary>
  38. /// Encodes and writes protocol message fields.
  39. /// </summary>
  40. /// <remarks>
  41. /// <para>
  42. /// This class is generally used by generated code to write appropriate
  43. /// primitives to the stream. It effectively encapsulates the lowest
  44. /// levels of protocol buffer format. Unlike some other implementations,
  45. /// this does not include combined "write tag and value" methods. Generated
  46. /// code knows the exact byte representations of the tags they're going to write,
  47. /// so there's no need to re-encode them each time. Manually-written code calling
  48. /// this class should just call one of the <c>WriteTag</c> overloads before each value.
  49. /// </para>
  50. /// <para>
  51. /// Repeated fields and map fields are not handled by this class; use <c>RepeatedField&lt;T&gt;</c>
  52. /// and <c>MapField&lt;TKey, TValue&gt;</c> to serialize such fields.
  53. /// </para>
  54. /// </remarks>
  55. public sealed partial class CodedOutputStream : IDisposable
  56. {
  57. // "Local" copy of Encoding.UTF8, for efficiency. (Yes, it makes a difference.)
  58. internal static readonly Encoding Utf8Encoding = Encoding.UTF8;
  59. /// <summary>
  60. /// The buffer size used by CreateInstance(Stream).
  61. /// </summary>
  62. public static readonly int DefaultBufferSize = 4096;
  63. private bool leaveOpen;
  64. private byte[] buffer;
  65. private int limit;
  66. private int position;
  67. private Stream output;
  68. #region Construction
  69. public byte[] Buffer
  70. {
  71. set
  72. {
  73. this.output = null;
  74. this.buffer = value;
  75. this.position = 0;
  76. this.limit = buffer.Length;
  77. leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
  78. }
  79. get
  80. {
  81. return buffer;
  82. }
  83. }
  84. public CodedOutputStream()
  85. {
  86. }
  87. /// <summary>
  88. /// Creates a new CodedOutputStream that writes directly to the given
  89. /// byte array. If more bytes are written than fit in the array,
  90. /// OutOfSpaceException will be thrown.
  91. /// </summary>
  92. public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
  93. {
  94. }
  95. /// <summary>
  96. /// Creates a new CodedOutputStream that writes directly to the given
  97. /// byte array slice. If more bytes are written than fit in the array,
  98. /// OutOfSpaceException will be thrown.
  99. /// </summary>
  100. private CodedOutputStream(byte[] buffer, int offset, int length)
  101. {
  102. this.output = null;
  103. this.buffer = buffer;
  104. this.position = offset;
  105. this.limit = offset + length;
  106. leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
  107. }
  108. private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
  109. {
  110. this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
  111. this.buffer = buffer;
  112. this.position = 0;
  113. this.limit = buffer.Length;
  114. this.leaveOpen = leaveOpen;
  115. }
  116. /// <summary>
  117. /// Creates a new <see cref="CodedOutputStream" /> which write to the given stream, and disposes of that
  118. /// stream when the returned <c>CodedOutputStream</c> is disposed.
  119. /// </summary>
  120. /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
  121. public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
  122. {
  123. }
  124. public void Reset(int offset)
  125. {
  126. this.position = offset;
  127. }
  128. /// <summary>
  129. /// Creates a new CodedOutputStream which write to the given stream and uses
  130. /// the specified buffer size.
  131. /// </summary>
  132. /// <param name="output">The stream to write to. It will be disposed when the returned <c>CodedOutputStream is disposed.</c></param>
  133. /// <param name="bufferSize">The size of buffer to use internally.</param>
  134. public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
  135. {
  136. }
  137. /// <summary>
  138. /// Creates a new CodedOutputStream which write to the given stream.
  139. /// </summary>
  140. /// <param name="output">The stream to write to.</param>
  141. /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
  142. /// if <c>false</c>, the provided stream is disposed as well.</param>
  143. public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)
  144. {
  145. }
  146. /// <summary>
  147. /// Creates a new CodedOutputStream which write to the given stream and uses
  148. /// the specified buffer size.
  149. /// </summary>
  150. /// <param name="output">The stream to write to.</param>
  151. /// <param name="bufferSize">The size of buffer to use internally.</param>
  152. /// <param name="leaveOpen">If <c>true</c>, <paramref name="output"/> is left open when the returned <c>CodedOutputStream</c> is disposed;
  153. /// if <c>false</c>, the provided stream is disposed as well.</param>
  154. public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
  155. {
  156. }
  157. #endregion
  158. /// <summary>
  159. /// Returns the current position in the stream, or the position in the output buffer
  160. /// </summary>
  161. public long Position
  162. {
  163. get
  164. {
  165. if (output != null)
  166. {
  167. return output.Position + position;
  168. }
  169. return position;
  170. }
  171. }
  172. #region Writing of values (not including tags)
  173. /// <summary>
  174. /// Writes a double field value, without a tag, to the stream.
  175. /// </summary>
  176. /// <param name="value">The value to write</param>
  177. public void WriteDouble(double value)
  178. {
  179. WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
  180. }
  181. /// <summary>
  182. /// Writes a float field value, without a tag, to the stream.
  183. /// </summary>
  184. /// <param name="value">The value to write</param>
  185. public void WriteFloat(float value)
  186. {
  187. byte[] rawBytes = BitConverter.GetBytes(value);
  188. if (!BitConverter.IsLittleEndian)
  189. {
  190. ByteArray.Reverse(rawBytes);
  191. }
  192. if (limit - position >= 4)
  193. {
  194. buffer[position++] = rawBytes[0];
  195. buffer[position++] = rawBytes[1];
  196. buffer[position++] = rawBytes[2];
  197. buffer[position++] = rawBytes[3];
  198. }
  199. else
  200. {
  201. WriteRawBytes(rawBytes, 0, 4);
  202. }
  203. }
  204. /// <summary>
  205. /// Writes a uint64 field value, without a tag, to the stream.
  206. /// </summary>
  207. /// <param name="value">The value to write</param>
  208. public void WriteUInt64(ulong value)
  209. {
  210. WriteRawVarint64(value);
  211. }
  212. /// <summary>
  213. /// Writes an int64 field value, without a tag, to the stream.
  214. /// </summary>
  215. /// <param name="value">The value to write</param>
  216. public void WriteInt64(long value)
  217. {
  218. WriteRawVarint64((ulong)value);
  219. }
  220. /// <summary>
  221. /// Writes an int32 field value, without a tag, to the stream.
  222. /// </summary>
  223. /// <param name="value">The value to write</param>
  224. public void WriteInt32(int value)
  225. {
  226. if (value >= 0)
  227. {
  228. WriteRawVarint32((uint)value);
  229. }
  230. else
  231. {
  232. // Must sign-extend.
  233. WriteRawVarint64((ulong)value);
  234. }
  235. }
  236. /// <summary>
  237. /// Writes a fixed64 field value, without a tag, to the stream.
  238. /// </summary>
  239. /// <param name="value">The value to write</param>
  240. public void WriteFixed64(ulong value)
  241. {
  242. WriteRawLittleEndian64(value);
  243. }
  244. /// <summary>
  245. /// Writes a fixed32 field value, without a tag, to the stream.
  246. /// </summary>
  247. /// <param name="value">The value to write</param>
  248. public void WriteFixed32(uint value)
  249. {
  250. WriteRawLittleEndian32(value);
  251. }
  252. /// <summary>
  253. /// Writes a bool field value, without a tag, to the stream.
  254. /// </summary>
  255. /// <param name="value">The value to write</param>
  256. public void WriteBool(bool value)
  257. {
  258. WriteRawByte(value ? (byte)1 : (byte)0);
  259. }
  260. /// <summary>
  261. /// Writes a string field value, without a tag, to the stream.
  262. /// The data is length-prefixed.
  263. /// </summary>
  264. /// <param name="value">The value to write</param>
  265. /// <param name="writeLength">是否同时写入字符串长度,默认为true</param>
  266. public void WriteString(string value, bool writeLength = true)
  267. {
  268. // Optimise the case where we have enough space to write
  269. // the string directly to the buffer, which should be common.
  270. int length = Utf8Encoding.GetByteCount(value);
  271. if (writeLength)
  272. {
  273. WriteLength(length);
  274. }
  275. if (limit - position >= length)
  276. {
  277. if (length == value.Length) // Must be all ASCII...
  278. {
  279. for (int i = 0; i < length; i++)
  280. {
  281. buffer[position + i] = (byte)value[i];
  282. }
  283. }
  284. else
  285. {
  286. Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
  287. }
  288. position += length;
  289. }
  290. else
  291. {
  292. byte[] bytes = Utf8Encoding.GetBytes(value);
  293. WriteRawBytes(bytes);
  294. }
  295. }
  296. ///// <summary>
  297. ///// Writes a message, without a tag, to the stream.
  298. ///// The data is length-prefixed.
  299. ///// </summary>
  300. ///// <param name="value">The value to write</param>
  301. //public void WriteMessage(IMessage value)
  302. //{
  303. // WriteLength(value.CalculateSize());
  304. // value.WriteTo(this);
  305. //}
  306. /// <summary>
  307. /// Writes a message, without a tag, to the stream.
  308. /// The data is length-prefixed.
  309. /// </summary>
  310. /// <param name="value">The value to write</param>
  311. public void WriteMessage(IMsgParser value)
  312. {
  313. WriteLength(value.CalculateSize());
  314. value.WriteTo(this);
  315. }
  316. /// <summary>
  317. /// Write a byte string, without a tag, to the stream.
  318. /// The data is length-prefixed.
  319. /// </summary>
  320. /// <param name="value">The value to write</param>
  321. public void WriteBytes(ByteString value)
  322. {
  323. WriteLength(value.Length);
  324. value.WriteRawBytesTo(this);
  325. }
  326. /// <summary>
  327. /// Writes a uint32 value, without a tag, to the stream.
  328. /// </summary>
  329. /// <param name="value">The value to write</param>
  330. public void WriteUInt32(uint value)
  331. {
  332. WriteRawVarint32(value);
  333. }
  334. /// <summary>
  335. /// Writes an enum value, without a tag, to the stream.
  336. /// </summary>
  337. /// <param name="value">The value to write</param>
  338. public void WriteEnum(int value)
  339. {
  340. WriteInt32(value);
  341. }
  342. /// <summary>
  343. /// Writes an sfixed32 value, without a tag, to the stream.
  344. /// </summary>
  345. /// <param name="value">The value to write.</param>
  346. public void WriteSFixed32(int value)
  347. {
  348. WriteRawLittleEndian32((uint)value);
  349. }
  350. /// <summary>
  351. /// Writes an sfixed64 value, without a tag, to the stream.
  352. /// </summary>
  353. /// <param name="value">The value to write</param>
  354. public void WriteSFixed64(long value)
  355. {
  356. WriteRawLittleEndian64((ulong)value);
  357. }
  358. /// <summary>
  359. /// Writes an sint32 value, without a tag, to the stream.
  360. /// </summary>
  361. /// <param name="value">The value to write</param>
  362. public void WriteSInt32(int value)
  363. {
  364. WriteRawVarint32(EncodeZigZag32(value));
  365. }
  366. /// <summary>
  367. /// Writes an sint64 value, without a tag, to the stream.
  368. /// </summary>
  369. /// <param name="value">The value to write</param>
  370. public void WriteSInt64(long value)
  371. {
  372. WriteRawVarint64(EncodeZigZag64(value));
  373. }
  374. /// <summary>
  375. /// Writes a length (in bytes) for length-delimited data.
  376. /// </summary>
  377. /// <remarks>
  378. /// This method simply writes a rawint, but exists for clarity in calling code.
  379. /// </remarks>
  380. /// <param name="length">Length value, in bytes.</param>
  381. public void WriteLength(int length)
  382. {
  383. WriteRawVarint32((uint)length);
  384. }
  385. #endregion
  386. #region Raw tag writing
  387. /// <summary>
  388. /// Encodes and writes a tag.
  389. /// </summary>
  390. /// <param name="fieldNumber">The number of the field to write the tag for</param>
  391. /// <param name="type">The wire format type of the tag to write</param>
  392. public void WriteTag(int fieldNumber, WireFormat.WireType type)
  393. {
  394. WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
  395. }
  396. /// <summary>
  397. /// Writes an already-encoded tag.
  398. /// </summary>
  399. /// <param name="tag">The encoded tag</param>
  400. public void WriteTag(uint tag)
  401. {
  402. WriteRawVarint32(tag);
  403. }
  404. /// <summary>
  405. /// Writes the given single-byte tag directly to the stream.
  406. /// </summary>
  407. /// <param name="b1">The encoded tag</param>
  408. public void WriteRawTag(byte b1)
  409. {
  410. WriteRawByte(b1);
  411. }
  412. /// <summary>
  413. /// Writes the given two-byte tag directly to the stream.
  414. /// </summary>
  415. /// <param name="b1">The first byte of the encoded tag</param>
  416. /// <param name="b2">The second byte of the encoded tag</param>
  417. public void WriteRawTag(byte b1, byte b2)
  418. {
  419. WriteRawByte(b1);
  420. WriteRawByte(b2);
  421. }
  422. /// <summary>
  423. /// Writes the given three-byte tag directly to the stream.
  424. /// </summary>
  425. /// <param name="b1">The first byte of the encoded tag</param>
  426. /// <param name="b2">The second byte of the encoded tag</param>
  427. /// <param name="b3">The third byte of the encoded tag</param>
  428. public void WriteRawTag(byte b1, byte b2, byte b3)
  429. {
  430. WriteRawByte(b1);
  431. WriteRawByte(b2);
  432. WriteRawByte(b3);
  433. }
  434. /// <summary>
  435. /// Writes the given four-byte tag directly to the stream.
  436. /// </summary>
  437. /// <param name="b1">The first byte of the encoded tag</param>
  438. /// <param name="b2">The second byte of the encoded tag</param>
  439. /// <param name="b3">The third byte of the encoded tag</param>
  440. /// <param name="b4">The fourth byte of the encoded tag</param>
  441. public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
  442. {
  443. WriteRawByte(b1);
  444. WriteRawByte(b2);
  445. WriteRawByte(b3);
  446. WriteRawByte(b4);
  447. }
  448. /// <summary>
  449. /// Writes the given five-byte tag directly to the stream.
  450. /// </summary>
  451. /// <param name="b1">The first byte of the encoded tag</param>
  452. /// <param name="b2">The second byte of the encoded tag</param>
  453. /// <param name="b3">The third byte of the encoded tag</param>
  454. /// <param name="b4">The fourth byte of the encoded tag</param>
  455. /// <param name="b5">The fifth byte of the encoded tag</param>
  456. public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
  457. {
  458. WriteRawByte(b1);
  459. WriteRawByte(b2);
  460. WriteRawByte(b3);
  461. WriteRawByte(b4);
  462. WriteRawByte(b5);
  463. }
  464. #endregion
  465. #region Underlying writing primitives
  466. /// <summary>
  467. /// Writes a 32 bit value as a varint. The fast route is taken when
  468. /// there's enough buffer space left to whizz through without checking
  469. /// for each byte; otherwise, we resort to calling WriteRawByte each time.
  470. /// </summary>
  471. internal void WriteRawVarint32(uint value)
  472. {
  473. // Optimize for the common case of a single byte value
  474. if (value < 128 && position < limit)
  475. {
  476. buffer[position++] = (byte)value;
  477. return;
  478. }
  479. while (value > 127 && position < limit)
  480. {
  481. buffer[position++] = (byte)((value & 0x7F) | 0x80);
  482. value >>= 7;
  483. }
  484. while (value > 127)
  485. {
  486. WriteRawByte((byte)((value & 0x7F) | 0x80));
  487. value >>= 7;
  488. }
  489. if (position < limit)
  490. {
  491. buffer[position++] = (byte)value;
  492. }
  493. else
  494. {
  495. WriteRawByte((byte)value);
  496. }
  497. }
  498. internal void WriteRawVarint64(ulong value)
  499. {
  500. while (value > 127 && position < limit)
  501. {
  502. buffer[position++] = (byte)((value & 0x7F) | 0x80);
  503. value >>= 7;
  504. }
  505. while (value > 127)
  506. {
  507. WriteRawByte((byte)((value & 0x7F) | 0x80));
  508. value >>= 7;
  509. }
  510. if (position < limit)
  511. {
  512. buffer[position++] = (byte)value;
  513. }
  514. else
  515. {
  516. WriteRawByte((byte)value);
  517. }
  518. }
  519. internal void WriteRawLittleEndian32(uint value)
  520. {
  521. if (position + 4 > limit)
  522. {
  523. WriteRawByte((byte)value);
  524. WriteRawByte((byte)(value >> 8));
  525. WriteRawByte((byte)(value >> 16));
  526. WriteRawByte((byte)(value >> 24));
  527. }
  528. else
  529. {
  530. buffer[position++] = ((byte)value);
  531. buffer[position++] = ((byte)(value >> 8));
  532. buffer[position++] = ((byte)(value >> 16));
  533. buffer[position++] = ((byte)(value >> 24));
  534. }
  535. }
  536. internal void WriteRawLittleEndian64(ulong value)
  537. {
  538. if (position + 8 > limit)
  539. {
  540. WriteRawByte((byte)value);
  541. WriteRawByte((byte)(value >> 8));
  542. WriteRawByte((byte)(value >> 16));
  543. WriteRawByte((byte)(value >> 24));
  544. WriteRawByte((byte)(value >> 32));
  545. WriteRawByte((byte)(value >> 40));
  546. WriteRawByte((byte)(value >> 48));
  547. WriteRawByte((byte)(value >> 56));
  548. }
  549. else
  550. {
  551. buffer[position++] = ((byte)value);
  552. buffer[position++] = ((byte)(value >> 8));
  553. buffer[position++] = ((byte)(value >> 16));
  554. buffer[position++] = ((byte)(value >> 24));
  555. buffer[position++] = ((byte)(value >> 32));
  556. buffer[position++] = ((byte)(value >> 40));
  557. buffer[position++] = ((byte)(value >> 48));
  558. buffer[position++] = ((byte)(value >> 56));
  559. }
  560. }
  561. internal void WriteRawByte(byte value)
  562. {
  563. if (position == limit)
  564. {
  565. RefreshBuffer();
  566. }
  567. buffer[position++] = value;
  568. }
  569. internal void WriteRawByte(uint value)
  570. {
  571. WriteRawByte((byte)value);
  572. }
  573. /// <summary>
  574. /// Writes out an array of bytes.
  575. /// </summary>
  576. internal void WriteRawBytes(byte[] value)
  577. {
  578. WriteRawBytes(value, 0, value.Length);
  579. }
  580. /// <summary>
  581. /// Writes out part of an array of bytes.
  582. /// </summary>
  583. internal void WriteRawBytes(byte[] value, int offset, int length)
  584. {
  585. if (limit - position >= length)
  586. {
  587. ByteArray.Copy(value, offset, buffer, position, length);
  588. // We have room in the current buffer.
  589. position += length;
  590. }
  591. else
  592. {
  593. // Write extends past current buffer. Fill the rest of this buffer and
  594. // flush.
  595. int bytesWritten = limit - position;
  596. ByteArray.Copy(value, offset, buffer, position, bytesWritten);
  597. offset += bytesWritten;
  598. length -= bytesWritten;
  599. position = limit;
  600. RefreshBuffer();
  601. // Now deal with the rest.
  602. // Since we have an output stream, this is our buffer
  603. // and buffer offset == 0
  604. if (length <= limit)
  605. {
  606. // Fits in new buffer.
  607. ByteArray.Copy(value, offset, buffer, 0, length);
  608. position = length;
  609. }
  610. else
  611. {
  612. // Write is very big. Let's do it all at once.
  613. output.Write(value, offset, length);
  614. }
  615. }
  616. }
  617. #endregion
  618. /// <summary>
  619. /// Encode a 32-bit value with ZigZag encoding.
  620. /// </summary>
  621. /// <remarks>
  622. /// ZigZag encodes signed integers into values that can be efficiently
  623. /// encoded with varint. (Otherwise, negative values must be
  624. /// sign-extended to 64 bits to be varint encoded, thus always taking
  625. /// 10 bytes on the wire.)
  626. /// </remarks>
  627. internal static uint EncodeZigZag32(int n)
  628. {
  629. // Note: the right-shift must be arithmetic
  630. return (uint)((n << 1) ^ (n >> 31));
  631. }
  632. /// <summary>
  633. /// Encode a 64-bit value with ZigZag encoding.
  634. /// </summary>
  635. /// <remarks>
  636. /// ZigZag encodes signed integers into values that can be efficiently
  637. /// encoded with varint. (Otherwise, negative values must be
  638. /// sign-extended to 64 bits to be varint encoded, thus always taking
  639. /// 10 bytes on the wire.)
  640. /// </remarks>
  641. internal static ulong EncodeZigZag64(long n)
  642. {
  643. return (ulong)((n << 1) ^ (n >> 63));
  644. }
  645. private void RefreshBuffer()
  646. {
  647. if (output == null)
  648. {
  649. // We're writing to a single buffer.
  650. throw new OutOfSpaceException();
  651. }
  652. // Since we have an output stream, this is our buffer
  653. // and buffer offset == 0
  654. output.Write(buffer, 0, position);
  655. position = 0;
  656. }
  657. /// <summary>
  658. /// Flushes any buffered data and optionally closes the underlying stream, if any.
  659. /// </summary>
  660. /// <remarks>
  661. /// <para>
  662. /// By default, any underlying stream is closed by this method. To configure this behaviour,
  663. /// use a constructor overload with a <c>leaveOpen</c> parameter. If this instance does not
  664. /// have an underlying stream, this method does nothing.
  665. /// </para>
  666. /// <para>
  667. /// For the sake of efficiency, calling this method does not prevent future write calls - but
  668. /// if a later write ends up writing to a stream which has been disposed, that is likely to
  669. /// fail. It is recommend that you not call any other methods after this.
  670. /// </para>
  671. /// </remarks>
  672. public void Dispose()
  673. {
  674. Flush();
  675. if (!leaveOpen)
  676. {
  677. output.Dispose();
  678. }
  679. }
  680. /// <summary>
  681. /// Flushes any buffered data to the underlying stream (if there is one).
  682. /// </summary>
  683. public void Flush()
  684. {
  685. if (output != null)
  686. {
  687. RefreshBuffer();
  688. }
  689. }
  690. /// <summary>
  691. /// Verifies that SpaceLeft returns zero. It's common to create a byte array
  692. /// that is exactly big enough to hold a message, then write to it with
  693. /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
  694. /// the message was actually as big as expected, which can help bugs.
  695. /// </summary>
  696. public void CheckNoSpaceLeft()
  697. {
  698. if (SpaceLeft != 0)
  699. {
  700. throw new InvalidOperationException("Did not write as much data as expected.");
  701. }
  702. }
  703. /// <summary>
  704. /// If writing to a flat array, returns the space left in the array. Otherwise,
  705. /// throws an InvalidOperationException.
  706. /// </summary>
  707. public int SpaceLeft
  708. {
  709. get
  710. {
  711. if (output == null)
  712. {
  713. return limit - position;
  714. }
  715. else
  716. {
  717. throw new InvalidOperationException(
  718. "SpaceLeft can only be called on CodedOutputStreams that are " +
  719. "writing to a flat array.");
  720. }
  721. }
  722. }
  723. }
  724. }