CodedOutputStream.cs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2008 Google Inc. All rights reserved.
  4. // http://github.com/jskeet/dotnet-protobufs/
  5. // Original C++/Java/Python code:
  6. // http://code.google.com/p/protobuf/
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions are
  10. // met:
  11. //
  12. // * Redistributions of source code must retain the above copyright
  13. // notice, this list of conditions and the following disclaimer.
  14. // * Redistributions in binary form must reproduce the above
  15. // copyright notice, this list of conditions and the following disclaimer
  16. // in the documentation and/or other materials provided with the
  17. // distribution.
  18. // * Neither the name of Google Inc. nor the names of its
  19. // contributors may be used to endorse or promote products derived from
  20. // this software without specific prior written permission.
  21. //
  22. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. #endregion
  34. using System;
  35. using System.Collections;
  36. using System.IO;
  37. using System.Text;
  38. using Google.Protobuf.Collections;
  39. using Google.Protobuf.Descriptors;
  40. namespace Google.Protobuf
  41. {
  42. /// <summary>
  43. /// Encodes and writes protocol message fields.
  44. /// </summary>
  45. /// <remarks>
  46. /// This class contains two kinds of methods: methods that write specific
  47. /// protocol message constructs and field types (e.g. WriteTag and
  48. /// WriteInt32) and methods that write low-level values (e.g.
  49. /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
  50. /// messages, you should use the former methods, but if you are writing some
  51. /// other format of your own design, use the latter. The names of the former
  52. /// methods are taken from the protocol buffer type names, not .NET types.
  53. /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
  54. /// </remarks>
  55. public sealed partial class CodedOutputStream : ICodedOutputStream
  56. {
  57. private static readonly Encoding UTF8 = Encoding.UTF8;
  58. /// <summary>
  59. /// The buffer size used by CreateInstance(Stream).
  60. /// </summary>
  61. public static readonly int DefaultBufferSize = 4096;
  62. private readonly byte[] buffer;
  63. private readonly int limit;
  64. private int position;
  65. private readonly Stream output;
  66. #region Construction
  67. private CodedOutputStream(byte[] buffer, int offset, int length)
  68. {
  69. this.output = null;
  70. this.buffer = buffer;
  71. this.position = offset;
  72. this.limit = offset + length;
  73. }
  74. private CodedOutputStream(Stream output, byte[] buffer)
  75. {
  76. this.output = output;
  77. this.buffer = buffer;
  78. this.position = 0;
  79. this.limit = buffer.Length;
  80. }
  81. /// <summary>
  82. /// Creates a new CodedOutputStream which write to the given stream.
  83. /// </summary>
  84. public static CodedOutputStream CreateInstance(Stream output)
  85. {
  86. return CreateInstance(output, DefaultBufferSize);
  87. }
  88. /// <summary>
  89. /// Creates a new CodedOutputStream which write to the given stream and uses
  90. /// the specified buffer size.
  91. /// </summary>
  92. public static CodedOutputStream CreateInstance(Stream output, int bufferSize)
  93. {
  94. return new CodedOutputStream(output, new byte[bufferSize]);
  95. }
  96. /// <summary>
  97. /// Creates a new CodedOutputStream that writes directly to the given
  98. /// byte array. If more bytes are written than fit in the array,
  99. /// OutOfSpaceException will be thrown.
  100. /// </summary>
  101. public static CodedOutputStream CreateInstance(byte[] flatArray)
  102. {
  103. return CreateInstance(flatArray, 0, flatArray.Length);
  104. }
  105. /// <summary>
  106. /// Creates a new CodedOutputStream that writes directly to the given
  107. /// byte array slice. If more bytes are written than fit in the array,
  108. /// OutOfSpaceException will be thrown.
  109. /// </summary>
  110. public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length)
  111. {
  112. return new CodedOutputStream(flatArray, offset, length);
  113. }
  114. #endregion
  115. /// <summary>
  116. /// Returns the current position in the stream, or the position in the output buffer
  117. /// </summary>
  118. public long Position
  119. {
  120. get
  121. {
  122. if (output != null)
  123. {
  124. return output.Position + position;
  125. }
  126. return position;
  127. }
  128. }
  129. void ICodedOutputStream.WriteMessageStart() { }
  130. void ICodedOutputStream.WriteMessageEnd() { Flush(); }
  131. #region Writing of tags and fields
  132. // TODO(jonskeet): Do we need this?
  133. public void WriteField(FieldType fieldType, int fieldNumber, string fieldName, object value)
  134. {
  135. switch (fieldType)
  136. {
  137. case FieldType.String:
  138. WriteString(fieldNumber, fieldName, (string) value);
  139. break;
  140. case FieldType.Message:
  141. WriteMessage(fieldNumber, fieldName, (IMessage) value);
  142. break;
  143. case FieldType.Group:
  144. WriteGroup(fieldNumber, fieldName, (IMessage) value);
  145. break;
  146. case FieldType.Bytes:
  147. WriteBytes(fieldNumber, fieldName, (ByteString) value);
  148. break;
  149. case FieldType.Bool:
  150. WriteBool(fieldNumber, fieldName, (bool) value);
  151. break;
  152. case FieldType.Enum:
  153. throw new NotImplementedException();
  154. case FieldType.Int32:
  155. WriteInt32(fieldNumber, fieldName, (int) value);
  156. break;
  157. case FieldType.Int64:
  158. WriteInt64(fieldNumber, fieldName, (long) value);
  159. break;
  160. case FieldType.UInt32:
  161. WriteUInt32(fieldNumber, fieldName, (uint) value);
  162. break;
  163. case FieldType.UInt64:
  164. WriteUInt64(fieldNumber, fieldName, (ulong) value);
  165. break;
  166. case FieldType.SInt32:
  167. WriteSInt32(fieldNumber, fieldName, (int) value);
  168. break;
  169. case FieldType.SInt64:
  170. WriteSInt64(fieldNumber, fieldName, (long) value);
  171. break;
  172. case FieldType.Fixed32:
  173. WriteFixed32(fieldNumber, fieldName, (uint) value);
  174. break;
  175. case FieldType.Fixed64:
  176. WriteFixed64(fieldNumber, fieldName, (ulong) value);
  177. break;
  178. case FieldType.SFixed32:
  179. WriteSFixed32(fieldNumber, fieldName, (int) value);
  180. break;
  181. case FieldType.SFixed64:
  182. WriteSFixed64(fieldNumber, fieldName, (long) value);
  183. break;
  184. case FieldType.Double:
  185. WriteDouble(fieldNumber, fieldName, (double) value);
  186. break;
  187. case FieldType.Float:
  188. WriteFloat(fieldNumber, fieldName, (float) value);
  189. break;
  190. }
  191. }
  192. /// <summary>
  193. /// Writes a double field value, including tag, to the stream.
  194. /// </summary>
  195. public void WriteDouble(int fieldNumber, string fieldName, double value)
  196. {
  197. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  198. WriteDoubleNoTag(value);
  199. }
  200. /// <summary>
  201. /// Writes a float field value, including tag, to the stream.
  202. /// </summary>
  203. public void WriteFloat(int fieldNumber, string fieldName, float value)
  204. {
  205. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  206. WriteFloatNoTag(value);
  207. }
  208. /// <summary>
  209. /// Writes a uint64 field value, including tag, to the stream.
  210. /// </summary>
  211. public void WriteUInt64(int fieldNumber, string fieldName, ulong value)
  212. {
  213. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  214. WriteRawVarint64(value);
  215. }
  216. /// <summary>
  217. /// Writes an int64 field value, including tag, to the stream.
  218. /// </summary>
  219. public void WriteInt64(int fieldNumber, string fieldName, long value)
  220. {
  221. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  222. WriteRawVarint64((ulong) value);
  223. }
  224. /// <summary>
  225. /// Writes an int32 field value, including tag, to the stream.
  226. /// </summary>
  227. public void WriteInt32(int fieldNumber, string fieldName, int value)
  228. {
  229. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  230. if (value >= 0)
  231. {
  232. WriteRawVarint32((uint) value);
  233. }
  234. else
  235. {
  236. // Must sign-extend.
  237. WriteRawVarint64((ulong) value);
  238. }
  239. }
  240. /// <summary>
  241. /// Writes a fixed64 field value, including tag, to the stream.
  242. /// </summary>
  243. public void WriteFixed64(int fieldNumber, string fieldName, ulong value)
  244. {
  245. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  246. WriteRawLittleEndian64(value);
  247. }
  248. /// <summary>
  249. /// Writes a fixed32 field value, including tag, to the stream.
  250. /// </summary>
  251. public void WriteFixed32(int fieldNumber, string fieldName, uint value)
  252. {
  253. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  254. WriteRawLittleEndian32(value);
  255. }
  256. /// <summary>
  257. /// Writes a bool field value, including tag, to the stream.
  258. /// </summary>
  259. public void WriteBool(int fieldNumber, string fieldName, bool value)
  260. {
  261. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  262. WriteRawByte(value ? (byte) 1 : (byte) 0);
  263. }
  264. /// <summary>
  265. /// Writes a string field value, including tag, to the stream.
  266. /// </summary>
  267. public void WriteString(int fieldNumber, string fieldName, string value)
  268. {
  269. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  270. // Optimise the case where we have enough space to write
  271. // the string directly to the buffer, which should be common.
  272. int length = UTF8.GetByteCount(value);
  273. WriteRawVarint32((uint) length);
  274. if (limit - position >= length)
  275. {
  276. if (length == value.Length) // Must be all ASCII...
  277. {
  278. for (int i = 0; i < length; i++)
  279. {
  280. buffer[position + i] = (byte)value[i];
  281. }
  282. }
  283. else
  284. {
  285. UTF8.GetBytes(value, 0, value.Length, buffer, position);
  286. }
  287. position += length;
  288. }
  289. else
  290. {
  291. byte[] bytes = UTF8.GetBytes(value);
  292. WriteRawBytes(bytes);
  293. }
  294. }
  295. /// <summary>
  296. /// Writes a group field value, including tag, to the stream.
  297. /// </summary>
  298. public void WriteGroup(int fieldNumber, string fieldName, IMessage value)
  299. {
  300. WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
  301. value.WriteTo(this);
  302. WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
  303. }
  304. public void WriteMessage(int fieldNumber, string fieldName, IMessage value)
  305. {
  306. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  307. WriteRawVarint32((uint) value.CalculateSize());
  308. value.WriteTo(this);
  309. }
  310. public void WriteBytes(int fieldNumber, string fieldName, ByteString value)
  311. {
  312. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  313. WriteRawVarint32((uint) value.Length);
  314. value.WriteRawBytesTo(this);
  315. }
  316. public void WriteUInt32(int fieldNumber, string fieldName, uint value)
  317. {
  318. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  319. WriteRawVarint32(value);
  320. }
  321. public void WriteEnum<T>(int fieldNumber, string fieldName, T value) where T : struct, IComparable, IFormattable
  322. {
  323. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  324. WriteInt32NoTag(EnumHelper<T>.ToInt32(value));
  325. }
  326. public void WriteSFixed32(int fieldNumber, string fieldName, int value)
  327. {
  328. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  329. WriteRawLittleEndian32((uint) value);
  330. }
  331. public void WriteSFixed64(int fieldNumber, string fieldName, long value)
  332. {
  333. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  334. WriteRawLittleEndian64((ulong) value);
  335. }
  336. public void WriteSInt32(int fieldNumber, string fieldName, int value)
  337. {
  338. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  339. WriteRawVarint32(EncodeZigZag32(value));
  340. }
  341. public void WriteSInt64(int fieldNumber, string fieldName, long value)
  342. {
  343. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  344. WriteRawVarint64(EncodeZigZag64(value));
  345. }
  346. public void WriteMessageSetExtension(int fieldNumber, string fieldName, IMessage value)
  347. {
  348. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
  349. WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
  350. WriteMessage(WireFormat.MessageSetField.Message, "message", value);
  351. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
  352. }
  353. public void WriteMessageSetExtension(int fieldNumber, string fieldName, ByteString value)
  354. {
  355. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
  356. WriteUInt32(WireFormat.MessageSetField.TypeID, "type_id", (uint) fieldNumber);
  357. WriteBytes(WireFormat.MessageSetField.Message, "message", value);
  358. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
  359. }
  360. #endregion
  361. #region Writing of values without tags
  362. // TODO(jonskeet): Remove this?
  363. public void WriteFieldNoTag(FieldType fieldType, object value)
  364. {
  365. switch (fieldType)
  366. {
  367. case FieldType.String:
  368. WriteStringNoTag((string) value);
  369. break;
  370. case FieldType.Message:
  371. WriteMessageNoTag((IMessage) value);
  372. break;
  373. case FieldType.Group:
  374. WriteGroupNoTag((IMessage) value);
  375. break;
  376. case FieldType.Bytes:
  377. WriteBytesNoTag((ByteString) value);
  378. break;
  379. case FieldType.Bool:
  380. WriteBoolNoTag((bool) value);
  381. break;
  382. case FieldType.Enum:
  383. WriteEnumNoTag((int) value);
  384. break;
  385. case FieldType.Int32:
  386. WriteInt32NoTag((int) value);
  387. break;
  388. case FieldType.Int64:
  389. WriteInt64NoTag((long) value);
  390. break;
  391. case FieldType.UInt32:
  392. WriteUInt32NoTag((uint) value);
  393. break;
  394. case FieldType.UInt64:
  395. WriteUInt64NoTag((ulong) value);
  396. break;
  397. case FieldType.SInt32:
  398. WriteSInt32NoTag((int) value);
  399. break;
  400. case FieldType.SInt64:
  401. WriteSInt64NoTag((long) value);
  402. break;
  403. case FieldType.Fixed32:
  404. WriteFixed32NoTag((uint) value);
  405. break;
  406. case FieldType.Fixed64:
  407. WriteFixed64NoTag((ulong) value);
  408. break;
  409. case FieldType.SFixed32:
  410. WriteSFixed32NoTag((int) value);
  411. break;
  412. case FieldType.SFixed64:
  413. WriteSFixed64NoTag((long) value);
  414. break;
  415. case FieldType.Double:
  416. WriteDoubleNoTag((double) value);
  417. break;
  418. case FieldType.Float:
  419. WriteFloatNoTag((float) value);
  420. break;
  421. }
  422. }
  423. /// <summary>
  424. /// Writes a double field value, including tag, to the stream.
  425. /// </summary>
  426. public void WriteDoubleNoTag(double value)
  427. {
  428. WriteRawLittleEndian64((ulong)FrameworkPortability.DoubleToInt64(value));
  429. }
  430. /// <summary>
  431. /// Writes a float field value, without a tag, to the stream.
  432. /// </summary>
  433. public void WriteFloatNoTag(float value)
  434. {
  435. byte[] rawBytes = BitConverter.GetBytes(value);
  436. if (!BitConverter.IsLittleEndian)
  437. {
  438. ByteArray.Reverse(rawBytes);
  439. }
  440. if (limit - position >= 4)
  441. {
  442. buffer[position++] = rawBytes[0];
  443. buffer[position++] = rawBytes[1];
  444. buffer[position++] = rawBytes[2];
  445. buffer[position++] = rawBytes[3];
  446. }
  447. else
  448. {
  449. WriteRawBytes(rawBytes, 0, 4);
  450. }
  451. }
  452. /// <summary>
  453. /// Writes a uint64 field value, without a tag, to the stream.
  454. /// </summary>
  455. public void WriteUInt64NoTag(ulong value)
  456. {
  457. WriteRawVarint64(value);
  458. }
  459. /// <summary>
  460. /// Writes an int64 field value, without a tag, to the stream.
  461. /// </summary>
  462. public void WriteInt64NoTag(long value)
  463. {
  464. WriteRawVarint64((ulong) value);
  465. }
  466. /// <summary>
  467. /// Writes an int32 field value, without a tag, to the stream.
  468. /// </summary>
  469. public void WriteInt32NoTag(int value)
  470. {
  471. if (value >= 0)
  472. {
  473. WriteRawVarint32((uint) value);
  474. }
  475. else
  476. {
  477. // Must sign-extend.
  478. WriteRawVarint64((ulong) value);
  479. }
  480. }
  481. /// <summary>
  482. /// Writes a fixed64 field value, without a tag, to the stream.
  483. /// </summary>
  484. public void WriteFixed64NoTag(ulong value)
  485. {
  486. WriteRawLittleEndian64(value);
  487. }
  488. /// <summary>
  489. /// Writes a fixed32 field value, without a tag, to the stream.
  490. /// </summary>
  491. public void WriteFixed32NoTag(uint value)
  492. {
  493. WriteRawLittleEndian32(value);
  494. }
  495. /// <summary>
  496. /// Writes a bool field value, without a tag, to the stream.
  497. /// </summary>
  498. public void WriteBoolNoTag(bool value)
  499. {
  500. WriteRawByte(value ? (byte) 1 : (byte) 0);
  501. }
  502. /// <summary>
  503. /// Writes a string field value, without a tag, to the stream.
  504. /// </summary>
  505. public void WriteStringNoTag(string value)
  506. {
  507. // Optimise the case where we have enough space to write
  508. // the string directly to the buffer, which should be common.
  509. int length = Encoding.UTF8.GetByteCount(value);
  510. WriteRawVarint32((uint) length);
  511. if (limit - position >= length)
  512. {
  513. Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
  514. position += length;
  515. }
  516. else
  517. {
  518. byte[] bytes = Encoding.UTF8.GetBytes(value);
  519. WriteRawBytes(bytes);
  520. }
  521. }
  522. /// <summary>
  523. /// Writes a group field value, without a tag, to the stream.
  524. /// </summary>
  525. public void WriteGroupNoTag(IMessage value)
  526. {
  527. value.WriteTo(this);
  528. }
  529. public void WriteMessageNoTag(IMessage value)
  530. {
  531. WriteRawVarint32((uint) value.CalculateSize());
  532. value.WriteTo(this);
  533. }
  534. public void WriteBytesNoTag(ByteString value)
  535. {
  536. WriteRawVarint32((uint) value.Length);
  537. value.WriteRawBytesTo(this);
  538. }
  539. public void WriteUInt32NoTag(uint value)
  540. {
  541. WriteRawVarint32(value);
  542. }
  543. public void WriteEnumNoTag<T>(T value) where T : struct, IComparable, IFormattable
  544. {
  545. WriteInt32NoTag(EnumHelper<T>.ToInt32(value));
  546. }
  547. public void WriteSFixed32NoTag(int value)
  548. {
  549. WriteRawLittleEndian32((uint) value);
  550. }
  551. public void WriteSFixed64NoTag(long value)
  552. {
  553. WriteRawLittleEndian64((ulong) value);
  554. }
  555. public void WriteSInt32NoTag(int value)
  556. {
  557. WriteRawVarint32(EncodeZigZag32(value));
  558. }
  559. public void WriteSInt64NoTag(long value)
  560. {
  561. WriteRawVarint64(EncodeZigZag64(value));
  562. }
  563. #endregion
  564. #region Write array members
  565. // TODO(jonskeet): Remove?
  566. public void WriteArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
  567. {
  568. foreach (object element in list)
  569. {
  570. WriteField(fieldType, fieldNumber, fieldName, element);
  571. }
  572. }
  573. public void WriteGroupArray<T>(int fieldNumber, string fieldName, RepeatedField<T> list)
  574. where T : IMessage
  575. {
  576. foreach (IMessage value in list)
  577. {
  578. WriteGroup(fieldNumber, fieldName, value);
  579. }
  580. }
  581. public void WriteMessageArray<T>(int fieldNumber, string fieldName, RepeatedField<T> list)
  582. where T : IMessage
  583. {
  584. foreach (IMessage value in list)
  585. {
  586. WriteMessage(fieldNumber, fieldName, value);
  587. }
  588. }
  589. public void WriteStringArray(int fieldNumber, string fieldName, RepeatedField<string> list)
  590. {
  591. foreach (var value in list)
  592. {
  593. WriteString(fieldNumber, fieldName, value);
  594. }
  595. }
  596. public void WriteBytesArray(int fieldNumber, string fieldName, RepeatedField<ByteString> list)
  597. {
  598. foreach (var value in list)
  599. {
  600. WriteBytes(fieldNumber, fieldName, value);
  601. }
  602. }
  603. public void WriteBoolArray(int fieldNumber, string fieldName, RepeatedField<bool> list)
  604. {
  605. foreach (var value in list)
  606. {
  607. WriteBool(fieldNumber, fieldName, value);
  608. }
  609. }
  610. public void WriteInt32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  611. {
  612. foreach (var value in list)
  613. {
  614. WriteInt32(fieldNumber, fieldName, value);
  615. }
  616. }
  617. public void WriteSInt32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  618. {
  619. foreach (var value in list)
  620. {
  621. WriteSInt32(fieldNumber, fieldName, value);
  622. }
  623. }
  624. public void WriteUInt32Array(int fieldNumber, string fieldName, RepeatedField<uint> list)
  625. {
  626. foreach (var value in list)
  627. {
  628. WriteUInt32(fieldNumber, fieldName, value);
  629. }
  630. }
  631. public void WriteFixed32Array(int fieldNumber, string fieldName, RepeatedField<uint> list)
  632. {
  633. foreach (var value in list)
  634. {
  635. WriteFixed32(fieldNumber, fieldName, value);
  636. }
  637. }
  638. public void WriteSFixed32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  639. {
  640. foreach (var value in list)
  641. {
  642. WriteSFixed32(fieldNumber, fieldName, value);
  643. }
  644. }
  645. public void WriteInt64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  646. {
  647. foreach (var value in list)
  648. {
  649. WriteInt64(fieldNumber, fieldName, value);
  650. }
  651. }
  652. public void WriteSInt64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  653. {
  654. foreach (var value in list)
  655. {
  656. WriteSInt64(fieldNumber, fieldName, value);
  657. }
  658. }
  659. public void WriteUInt64Array(int fieldNumber, string fieldName, RepeatedField<ulong> list)
  660. {
  661. foreach (var value in list)
  662. {
  663. WriteUInt64(fieldNumber, fieldName, value);
  664. }
  665. }
  666. public void WriteFixed64Array(int fieldNumber, string fieldName, RepeatedField<ulong> list)
  667. {
  668. foreach (var value in list)
  669. {
  670. WriteFixed64(fieldNumber, fieldName, value);
  671. }
  672. }
  673. public void WriteSFixed64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  674. {
  675. foreach (var value in list)
  676. {
  677. WriteSFixed64(fieldNumber, fieldName, value);
  678. }
  679. }
  680. public void WriteDoubleArray(int fieldNumber, string fieldName, RepeatedField<double> list)
  681. {
  682. foreach (var value in list)
  683. {
  684. WriteDouble(fieldNumber, fieldName, value);
  685. }
  686. }
  687. public void WriteFloatArray(int fieldNumber, string fieldName, RepeatedField<float> list)
  688. {
  689. foreach (var value in list)
  690. {
  691. WriteFloat(fieldNumber, fieldName, value);
  692. }
  693. }
  694. public void WriteEnumArray<T>(int fieldNumber, string fieldName, RepeatedField<T> list)
  695. where T : struct, IComparable, IFormattable
  696. {
  697. foreach (T value in list)
  698. {
  699. WriteEnum(fieldNumber, fieldName, value);
  700. }
  701. }
  702. #endregion
  703. #region Write packed array members
  704. // TODO(jonskeet): Remove?
  705. public void WritePackedArray(FieldType fieldType, int fieldNumber, string fieldName, IEnumerable list)
  706. {
  707. int calculatedSize = 0;
  708. foreach (object element in list)
  709. {
  710. calculatedSize += ComputeFieldSizeNoTag(fieldType, element);
  711. }
  712. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  713. WriteRawVarint32((uint) calculatedSize);
  714. foreach (object element in list)
  715. {
  716. WriteFieldNoTag(fieldType, element);
  717. }
  718. }
  719. // TODO(jonskeet): A lot of these are really inefficient, due to method group conversions. Fix!
  720. public void WritePackedBoolArray(int fieldNumber, string fieldName, RepeatedField<bool> list)
  721. {
  722. if (list.Count == 0)
  723. {
  724. return;
  725. }
  726. uint size = (uint)list.Count;
  727. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  728. WriteRawVarint32(size);
  729. foreach (var value in list)
  730. {
  731. WriteBoolNoTag(value);
  732. }
  733. }
  734. public void WritePackedInt32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  735. {
  736. if (list.Count == 0)
  737. {
  738. return;
  739. }
  740. uint size = list.CalculateSize(ComputeInt32SizeNoTag);
  741. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  742. WriteRawVarint32(size);
  743. foreach (var value in list)
  744. {
  745. WriteInt32NoTag(value);
  746. }
  747. }
  748. public void WritePackedSInt32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  749. {
  750. if (list.Count == 0)
  751. {
  752. return;
  753. }
  754. uint size = list.CalculateSize(ComputeSInt32SizeNoTag);
  755. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  756. WriteRawVarint32(size);
  757. foreach (var value in list)
  758. {
  759. WriteSInt32NoTag(value);
  760. }
  761. }
  762. public void WritePackedUInt32Array(int fieldNumber, string fieldName, RepeatedField<uint> list)
  763. {
  764. if (list.Count == 0)
  765. {
  766. return;
  767. }
  768. uint size = list.CalculateSize(ComputeUInt32SizeNoTag);
  769. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  770. WriteRawVarint32(size);
  771. foreach (var value in list)
  772. {
  773. WriteUInt32NoTag(value);
  774. }
  775. }
  776. public void WritePackedFixed32Array(int fieldNumber, string fieldName, RepeatedField<uint> list)
  777. {
  778. if (list.Count == 0)
  779. {
  780. return;
  781. }
  782. uint size = (uint) list.Count * 4;
  783. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  784. WriteRawVarint32(size);
  785. foreach (var value in list)
  786. {
  787. WriteFixed32NoTag(value);
  788. }
  789. }
  790. public void WritePackedSFixed32Array(int fieldNumber, string fieldName, RepeatedField<int> list)
  791. {
  792. if (list.Count == 0)
  793. {
  794. return;
  795. }
  796. uint size = (uint) list.Count * 4;
  797. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  798. WriteRawVarint32(size);
  799. foreach (var value in list)
  800. {
  801. WriteSFixed32NoTag(value);
  802. }
  803. }
  804. public void WritePackedInt64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  805. {
  806. if (list.Count == 0)
  807. {
  808. return;
  809. }
  810. uint size = list.CalculateSize(ComputeInt64SizeNoTag);
  811. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  812. WriteRawVarint32(size);
  813. foreach (var value in list)
  814. {
  815. WriteInt64NoTag(value);
  816. }
  817. }
  818. public void WritePackedSInt64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  819. {
  820. if (list.Count == 0)
  821. {
  822. return;
  823. }
  824. uint size = list.CalculateSize(ComputeSInt64SizeNoTag);
  825. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  826. WriteRawVarint32(size);
  827. foreach (var value in list)
  828. {
  829. WriteSInt64NoTag(value);
  830. }
  831. }
  832. public void WritePackedUInt64Array(int fieldNumber, string fieldName, RepeatedField<ulong> list)
  833. {
  834. if (list.Count == 0)
  835. {
  836. return;
  837. }
  838. uint size = list.CalculateSize(ComputeUInt64SizeNoTag);
  839. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  840. WriteRawVarint32(size);
  841. foreach (var value in list)
  842. {
  843. WriteUInt64NoTag(value);
  844. }
  845. }
  846. public void WritePackedFixed64Array(int fieldNumber, string fieldName, RepeatedField<ulong> list)
  847. {
  848. if (list.Count == 0)
  849. {
  850. return;
  851. }
  852. uint size = (uint) list.Count * 8;
  853. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  854. WriteRawVarint32(size);
  855. foreach (var value in list)
  856. {
  857. WriteFixed64NoTag(value);
  858. }
  859. }
  860. public void WritePackedSFixed64Array(int fieldNumber, string fieldName, RepeatedField<long> list)
  861. {
  862. if (list.Count == 0)
  863. {
  864. return;
  865. }
  866. uint size = (uint) list.Count * 8;
  867. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  868. WriteRawVarint32(size);
  869. foreach (var value in list)
  870. {
  871. WriteSFixed64NoTag(value);
  872. }
  873. }
  874. public void WritePackedDoubleArray(int fieldNumber, string fieldName, RepeatedField<double> list)
  875. {
  876. if (list.Count == 0)
  877. {
  878. return;
  879. }
  880. uint size = (uint) list.Count * 8;
  881. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  882. WriteRawVarint32(size);
  883. foreach (var value in list)
  884. {
  885. WriteDoubleNoTag(value);
  886. }
  887. }
  888. public void WritePackedFloatArray(int fieldNumber, string fieldName, RepeatedField<float> list)
  889. {
  890. if (list.Count == 0)
  891. {
  892. return;
  893. }
  894. uint size = (uint) list.Count * 4;
  895. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  896. WriteRawVarint32(size);
  897. foreach (var value in list)
  898. {
  899. WriteFloatNoTag(value);
  900. }
  901. }
  902. public void WritePackedEnumArray<T>(int fieldNumber, string fieldName, RepeatedField<T> list)
  903. where T : struct, IComparable, IFormattable
  904. {
  905. if (list.Count == 0)
  906. {
  907. return;
  908. }
  909. uint size = list.CalculateSize(ComputeEnumSizeNoTag);
  910. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  911. WriteRawVarint32(size);
  912. foreach (T value in list)
  913. {
  914. WriteEnumNoTag(value);
  915. }
  916. }
  917. #endregion
  918. #region Underlying writing primitives
  919. /// <summary>
  920. /// Encodes and writes a tag.
  921. /// </summary>
  922. public void WriteTag(int fieldNumber, WireFormat.WireType type)
  923. {
  924. WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
  925. }
  926. /// <summary>
  927. /// Writes a 32 bit value as a varint. The fast route is taken when
  928. /// there's enough buffer space left to whizz through without checking
  929. /// for each byte; otherwise, we resort to calling WriteRawByte each time.
  930. /// </summary>
  931. public void WriteRawVarint32(uint value)
  932. {
  933. // Optimize for the common case of a single byte value
  934. if (value < 128 && position < limit)
  935. {
  936. buffer[position++] = (byte)value;
  937. return;
  938. }
  939. while (value > 127 && position < limit)
  940. {
  941. buffer[position++] = (byte) ((value & 0x7F) | 0x80);
  942. value >>= 7;
  943. }
  944. while (value > 127)
  945. {
  946. WriteRawByte((byte) ((value & 0x7F) | 0x80));
  947. value >>= 7;
  948. }
  949. if (position < limit)
  950. {
  951. buffer[position++] = (byte) value;
  952. }
  953. else
  954. {
  955. WriteRawByte((byte) value);
  956. }
  957. }
  958. public void WriteRawVarint64(ulong value)
  959. {
  960. while (value > 127 && position < limit)
  961. {
  962. buffer[position++] = (byte) ((value & 0x7F) | 0x80);
  963. value >>= 7;
  964. }
  965. while (value > 127)
  966. {
  967. WriteRawByte((byte) ((value & 0x7F) | 0x80));
  968. value >>= 7;
  969. }
  970. if (position < limit)
  971. {
  972. buffer[position++] = (byte) value;
  973. }
  974. else
  975. {
  976. WriteRawByte((byte) value);
  977. }
  978. }
  979. public void WriteRawLittleEndian32(uint value)
  980. {
  981. if (position + 4 > limit)
  982. {
  983. WriteRawByte((byte) value);
  984. WriteRawByte((byte) (value >> 8));
  985. WriteRawByte((byte) (value >> 16));
  986. WriteRawByte((byte) (value >> 24));
  987. }
  988. else
  989. {
  990. buffer[position++] = ((byte) value);
  991. buffer[position++] = ((byte) (value >> 8));
  992. buffer[position++] = ((byte) (value >> 16));
  993. buffer[position++] = ((byte) (value >> 24));
  994. }
  995. }
  996. public void WriteRawLittleEndian64(ulong value)
  997. {
  998. if (position + 8 > limit)
  999. {
  1000. WriteRawByte((byte) value);
  1001. WriteRawByte((byte) (value >> 8));
  1002. WriteRawByte((byte) (value >> 16));
  1003. WriteRawByte((byte) (value >> 24));
  1004. WriteRawByte((byte) (value >> 32));
  1005. WriteRawByte((byte) (value >> 40));
  1006. WriteRawByte((byte) (value >> 48));
  1007. WriteRawByte((byte) (value >> 56));
  1008. }
  1009. else
  1010. {
  1011. buffer[position++] = ((byte) value);
  1012. buffer[position++] = ((byte) (value >> 8));
  1013. buffer[position++] = ((byte) (value >> 16));
  1014. buffer[position++] = ((byte) (value >> 24));
  1015. buffer[position++] = ((byte) (value >> 32));
  1016. buffer[position++] = ((byte) (value >> 40));
  1017. buffer[position++] = ((byte) (value >> 48));
  1018. buffer[position++] = ((byte) (value >> 56));
  1019. }
  1020. }
  1021. public void WriteRawByte(byte value)
  1022. {
  1023. if (position == limit)
  1024. {
  1025. RefreshBuffer();
  1026. }
  1027. buffer[position++] = value;
  1028. }
  1029. public void WriteRawByte(uint value)
  1030. {
  1031. WriteRawByte((byte) value);
  1032. }
  1033. /// <summary>
  1034. /// Writes out an array of bytes.
  1035. /// </summary>
  1036. public void WriteRawBytes(byte[] value)
  1037. {
  1038. WriteRawBytes(value, 0, value.Length);
  1039. }
  1040. /// <summary>
  1041. /// Writes out part of an array of bytes.
  1042. /// </summary>
  1043. public void WriteRawBytes(byte[] value, int offset, int length)
  1044. {
  1045. if (limit - position >= length)
  1046. {
  1047. ByteArray.Copy(value, offset, buffer, position, length);
  1048. // We have room in the current buffer.
  1049. position += length;
  1050. }
  1051. else
  1052. {
  1053. // Write extends past current buffer. Fill the rest of this buffer and
  1054. // flush.
  1055. int bytesWritten = limit - position;
  1056. ByteArray.Copy(value, offset, buffer, position, bytesWritten);
  1057. offset += bytesWritten;
  1058. length -= bytesWritten;
  1059. position = limit;
  1060. RefreshBuffer();
  1061. // Now deal with the rest.
  1062. // Since we have an output stream, this is our buffer
  1063. // and buffer offset == 0
  1064. if (length <= limit)
  1065. {
  1066. // Fits in new buffer.
  1067. ByteArray.Copy(value, offset, buffer, 0, length);
  1068. position = length;
  1069. }
  1070. else
  1071. {
  1072. // Write is very big. Let's do it all at once.
  1073. output.Write(value, offset, length);
  1074. }
  1075. }
  1076. }
  1077. #endregion
  1078. /// <summary>
  1079. /// Encode a 32-bit value with ZigZag encoding.
  1080. /// </summary>
  1081. /// <remarks>
  1082. /// ZigZag encodes signed integers into values that can be efficiently
  1083. /// encoded with varint. (Otherwise, negative values must be
  1084. /// sign-extended to 64 bits to be varint encoded, thus always taking
  1085. /// 10 bytes on the wire.)
  1086. /// </remarks>
  1087. public static uint EncodeZigZag32(int n)
  1088. {
  1089. // Note: the right-shift must be arithmetic
  1090. return (uint) ((n << 1) ^ (n >> 31));
  1091. }
  1092. /// <summary>
  1093. /// Encode a 64-bit value with ZigZag encoding.
  1094. /// </summary>
  1095. /// <remarks>
  1096. /// ZigZag encodes signed integers into values that can be efficiently
  1097. /// encoded with varint. (Otherwise, negative values must be
  1098. /// sign-extended to 64 bits to be varint encoded, thus always taking
  1099. /// 10 bytes on the wire.)
  1100. /// </remarks>
  1101. public static ulong EncodeZigZag64(long n)
  1102. {
  1103. return (ulong) ((n << 1) ^ (n >> 63));
  1104. }
  1105. private void RefreshBuffer()
  1106. {
  1107. if (output == null)
  1108. {
  1109. // We're writing to a single buffer.
  1110. throw new OutOfSpaceException();
  1111. }
  1112. // Since we have an output stream, this is our buffer
  1113. // and buffer offset == 0
  1114. output.Write(buffer, 0, position);
  1115. position = 0;
  1116. }
  1117. /// <summary>
  1118. /// Indicates that a CodedOutputStream wrapping a flat byte array
  1119. /// ran out of space.
  1120. /// </summary>
  1121. public sealed class OutOfSpaceException : IOException
  1122. {
  1123. internal OutOfSpaceException()
  1124. : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
  1125. {
  1126. }
  1127. }
  1128. public void Flush()
  1129. {
  1130. if (output != null)
  1131. {
  1132. RefreshBuffer();
  1133. }
  1134. }
  1135. /// <summary>
  1136. /// Verifies that SpaceLeft returns zero. It's common to create a byte array
  1137. /// that is exactly big enough to hold a message, then write to it with
  1138. /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
  1139. /// the message was actually as big as expected, which can help bugs.
  1140. /// </summary>
  1141. public void CheckNoSpaceLeft()
  1142. {
  1143. if (SpaceLeft != 0)
  1144. {
  1145. throw new InvalidOperationException("Did not write as much data as expected.");
  1146. }
  1147. }
  1148. /// <summary>
  1149. /// If writing to a flat array, returns the space left in the array. Otherwise,
  1150. /// throws an InvalidOperationException.
  1151. /// </summary>
  1152. public int SpaceLeft
  1153. {
  1154. get
  1155. {
  1156. if (output == null)
  1157. {
  1158. return limit - position;
  1159. }
  1160. else
  1161. {
  1162. throw new InvalidOperationException(
  1163. "SpaceLeft can only be called on CodedOutputStreams that are " +
  1164. "writing to a flat array.");
  1165. }
  1166. }
  1167. }
  1168. }
  1169. }