CodedOutputStream.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc.
  3. // http://code.google.com/p/protobuf/
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. using System;
  17. using System.IO;
  18. using System.Text;
  19. namespace Google.ProtocolBuffers {
  20. /// <summary>
  21. /// Encodes and writes protocol message fields.
  22. /// </summary>
  23. /// <remarks>
  24. /// This class contains two kinds of methods: methods that write specific
  25. /// protocol message constructs and field types (e.g. WriteTag and
  26. /// WriteInt32) and methods that write low-level values (e.g.
  27. /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
  28. /// messages, you should use the former methods, but if you are writing some
  29. /// other format of your own design, use the latter. The names of the former
  30. /// methods are taken from the protocol buffer type names, not .NET types.
  31. /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
  32. /// </remarks>
  33. public sealed class CodedOutputStream {
  34. /// <summary>
  35. /// The buffer size used by CreateInstance(Stream).
  36. /// </summary>
  37. public static readonly int DefaultBufferSize = 4096;
  38. private readonly byte[] buffer;
  39. private readonly int limit;
  40. private int position;
  41. private readonly Stream output;
  42. #region Construction
  43. private CodedOutputStream(byte[] buffer, int offset, int length) {
  44. this.output = null;
  45. this.buffer = buffer;
  46. this.position = offset;
  47. this.limit = offset + length;
  48. }
  49. private CodedOutputStream(Stream output, byte[] buffer) {
  50. this.output = output;
  51. this.buffer = buffer;
  52. this.position = 0;
  53. this.limit = buffer.Length;
  54. }
  55. /// <summary>
  56. /// Creates a new CodedOutputStream which write to the given stream.
  57. /// </summary>
  58. public static CodedOutputStream CreateInstance(Stream output) {
  59. return CreateInstance(output, DefaultBufferSize);
  60. }
  61. /// <summary>
  62. /// Creates a new CodedOutputStream which write to the given stream and uses
  63. /// the specified buffer size.
  64. /// </summary>
  65. public static CodedOutputStream CreateInstance(Stream output, int bufferSize) {
  66. return new CodedOutputStream(output, new byte[bufferSize]);
  67. }
  68. /// <summary>
  69. /// Creates a new CodedOutputStream that writes directly to the given
  70. /// byte array. If more bytes are written than fit in the array,
  71. /// OutOfSpaceException will be thrown.
  72. /// </summary>
  73. public static CodedOutputStream CreateInstance(byte[] flatArray) {
  74. return CreateInstance(flatArray, 0, flatArray.Length);
  75. }
  76. /// <summary>
  77. /// Creates a new CodedOutputStream that writes directly to the given
  78. /// byte array slice. If more bytes are written than fit in the array,
  79. /// OutOfSpaceException will be thrown.
  80. /// </summary>
  81. public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length) {
  82. return new CodedOutputStream(flatArray, offset, length);
  83. }
  84. #endregion
  85. #region Writing of tags etc
  86. /// <summary>
  87. /// Writes a double field value, including tag, to the stream.
  88. /// </summary>
  89. public void WriteDouble(int fieldNumber, double value) {
  90. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  91. WriteRawLittleEndian64(BitConverter.DoubleToInt64Bits(value));
  92. }
  93. /// <summary>
  94. /// Writes a float field value, including tag, to the stream.
  95. /// </summary>
  96. public void WriteFloat(int fieldNumber, float value) {
  97. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  98. // FIXME: How do we convert a single to 32 bits? (Without unsafe code)
  99. //WriteRawLittleEndian32(BitConverter.SingleT(value));
  100. }
  101. /// <summary>
  102. /// Writes a uint64 field value, including tag, to the stream.
  103. /// </summary>
  104. public void WriteUInt64(int fieldNumber, ulong value) {
  105. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  106. WriteRawVarint64(value);
  107. }
  108. /// <summary>
  109. /// Writes an int64 field value, including tag, to the stream.
  110. /// </summary>
  111. public void WriteInt64(int fieldNumber, long value) {
  112. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  113. WriteRawVarint64((ulong)value);
  114. }
  115. /// <summary>
  116. /// Writes an int32 field value, including tag, to the stream.
  117. /// </summary>
  118. public void WriteInt32(int fieldNumber, int value) {
  119. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  120. if (value >= 0) {
  121. WriteRawVarint32((uint)value);
  122. } else {
  123. // Must sign-extend.
  124. WriteRawVarint64((ulong)value);
  125. }
  126. }
  127. /// <summary>
  128. /// Writes a fixed64 field value, including tag, to the stream.
  129. /// </summary>
  130. public void WriteFixed64(int fieldNumber, long value) {
  131. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  132. WriteRawLittleEndian64(value);
  133. }
  134. /// <summary>
  135. /// Writes a fixed32 field value, including tag, to the stream.
  136. /// </summary>
  137. public void WriteFixed32(int fieldNumber, int value) {
  138. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  139. WriteRawLittleEndian32(value);
  140. }
  141. /// <summary>
  142. /// Writes a bool field value, including tag, to the stream.
  143. /// </summary>
  144. public void WriteBool(int fieldNumber, bool value) {
  145. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  146. WriteRawByte(value ? (byte)1 : (byte)0);
  147. }
  148. /// <summary>
  149. /// Writes a string field value, including tag, to the stream.
  150. /// </summary>
  151. public void WriteString(int fieldNumber, string value) {
  152. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  153. // TODO(jonskeet): Optimise this if possible
  154. // Unfortunately there does not appear to be any way to tell Java to encode
  155. // UTF-8 directly into our buffer, so we have to let it create its own byte
  156. // array and then copy. In .NET we can do the same thing very easily,
  157. // so we don't need to worry about only writing one buffer at a time.
  158. // We can optimise later.
  159. byte[] bytes = Encoding.UTF8.GetBytes(value);
  160. WriteRawVarint32((uint)bytes.Length);
  161. WriteRawBytes(bytes);
  162. }
  163. /// <summary>
  164. /// Writes a group field value, including tag, to the stream.
  165. /// </summary>
  166. public void WriteGroup(int fieldNumber, IMessage value) {
  167. WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
  168. value.WriteTo(this);
  169. WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
  170. }
  171. public void WriteUnknownGroup(int fieldNumber, UnknownFieldSet value) {
  172. WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
  173. value.WriteTo(this);
  174. WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
  175. }
  176. public void WriteMessage(int fieldNumber, IMessage value) {
  177. WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
  178. WriteRawVarint32((uint)value.SerializedSize);
  179. value.WriteTo(this);
  180. }
  181. public void WriteBytes(int fieldNumber, ByteString value) {
  182. // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
  183. byte[] bytes = value.ToByteArray();
  184. WriteRawVarint32((uint)bytes.Length);
  185. WriteRawBytes(bytes);
  186. }
  187. public void WriteUInt32(int fieldNumber, uint value) {
  188. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  189. WriteRawVarint32(value);
  190. }
  191. public void WriteEnum(int fieldNumber, int value) {
  192. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  193. WriteRawVarint32((uint)value);
  194. }
  195. public void WriteSFixed32(int fieldNumber, int value) {
  196. WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
  197. WriteRawVarint32((uint)value);
  198. }
  199. public void WriteSFixed64(int fieldNumber, long value) {
  200. WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
  201. WriteRawVarint64((ulong)value);
  202. }
  203. public void WriteSInt32(int fieldNumber, int value) {
  204. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  205. WriteRawVarint32(EncodeZigZag32(value));
  206. }
  207. public void WriteSInt64(int fieldNumber, long value) {
  208. WriteTag(fieldNumber, WireFormat.WireType.Varint);
  209. WriteRawVarint64(EncodeZigZag64(value));
  210. }
  211. public void WriteMessageSetExtension(int fieldNumber, IMessage value) {
  212. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
  213. WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
  214. WriteMessage(WireFormat.MessageSetField.Message, value);
  215. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
  216. }
  217. public void WriteRawMessageSetExtension(int fieldNumber, ByteString value) {
  218. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
  219. WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
  220. WriteBytes(WireFormat.MessageSetField.Message, value);
  221. WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
  222. }
  223. public void WriteField(Descriptors.FieldDescriptor.Type fieldType, int fieldNumber, object value) {
  224. switch (fieldType) {
  225. case Descriptors.FieldDescriptor.Type.Double: WriteDouble(fieldNumber, (double)value); break;
  226. case Descriptors.FieldDescriptor.Type.Float: WriteFloat(fieldNumber, (float)value); break;
  227. case Descriptors.FieldDescriptor.Type.Int64: WriteInt64(fieldNumber, (long)value); break;
  228. case Descriptors.FieldDescriptor.Type.UInt64: WriteUInt64(fieldNumber, (ulong)value); break;
  229. case Descriptors.FieldDescriptor.Type.Int32: WriteInt32(fieldNumber, (int)value); break;
  230. case Descriptors.FieldDescriptor.Type.Fixed64: WriteFixed64(fieldNumber, (long)value); break;
  231. case Descriptors.FieldDescriptor.Type.Fixed32: WriteFixed32(fieldNumber, (int)value); break;
  232. case Descriptors.FieldDescriptor.Type.Bool: WriteBool(fieldNumber, (bool)value); break;
  233. case Descriptors.FieldDescriptor.Type.String: WriteString(fieldNumber, (string)value); break;
  234. case Descriptors.FieldDescriptor.Type.Group: WriteGroup(fieldNumber, (IMessage)value); break;
  235. case Descriptors.FieldDescriptor.Type.Message: WriteMessage(fieldNumber, (IMessage)value); break;
  236. case Descriptors.FieldDescriptor.Type.Bytes: WriteBytes(fieldNumber, (ByteString)value); break;
  237. case Descriptors.FieldDescriptor.Type.UInt32: WriteUInt32(fieldNumber, (uint)value); break;
  238. case Descriptors.FieldDescriptor.Type.SFixed32: WriteSFixed32(fieldNumber, (int)value); break;
  239. case Descriptors.FieldDescriptor.Type.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
  240. case Descriptors.FieldDescriptor.Type.SInt32: WriteSInt32(fieldNumber, (int)value); break;
  241. case Descriptors.FieldDescriptor.Type.SInt64: WriteSInt64(fieldNumber, (long)value); break;
  242. case Descriptors.FieldDescriptor.Type.Enum: WriteEnum(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
  243. break;
  244. }
  245. }
  246. #endregion
  247. #region Underlying writing primitives
  248. /// <summary>
  249. /// Encodes and writes a tag.
  250. /// </summary>
  251. public void WriteTag(int fieldNumber, WireFormat.WireType type) {
  252. WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
  253. }
  254. public void WriteRawVarint32(uint value) {
  255. while (true) {
  256. if ((value & ~0x7F) == 0) {
  257. WriteRawByte(value);
  258. return;
  259. } else {
  260. WriteRawByte((value & 0x7F) | 0x80);
  261. value >>= 7;
  262. }
  263. }
  264. }
  265. public void WriteRawVarint64(ulong value) {
  266. while (true) {
  267. if ((value & ~0x7FUL) == 0) {
  268. WriteRawByte((uint)value);
  269. return;
  270. } else {
  271. WriteRawByte(((uint)value & 0x7F) | 0x80);
  272. value >>= 7;
  273. }
  274. }
  275. }
  276. public void WriteRawLittleEndian32(int value) {
  277. WriteRawByte((byte)value);
  278. WriteRawByte((byte)(value >> 8));
  279. WriteRawByte((byte)(value >> 16));
  280. WriteRawByte((byte)(value >> 24));
  281. }
  282. public void WriteRawLittleEndian64(long value) {
  283. WriteRawByte((byte)value);
  284. WriteRawByte((byte)(value >> 8));
  285. WriteRawByte((byte)(value >> 16));
  286. WriteRawByte((byte)(value >> 24));
  287. WriteRawByte((byte)(value >> 32));
  288. WriteRawByte((byte)(value >> 40));
  289. WriteRawByte((byte)(value >> 48));
  290. WriteRawByte((byte)(value >> 56));
  291. }
  292. public void WriteRawByte(byte value) {
  293. if (position == limit) {
  294. RefreshBuffer();
  295. }
  296. buffer[position++] = value;
  297. }
  298. public void WriteRawByte(uint value) {
  299. WriteRawByte((byte)value);
  300. }
  301. /// <summary>
  302. /// Writes out an array of bytes.
  303. /// </summary>
  304. public void WriteRawBytes(byte[] value) {
  305. WriteRawBytes(value, 0, value.Length);
  306. }
  307. /// <summary>
  308. /// Writes out part of an array of bytes.
  309. /// </summary>
  310. public void WriteRawBytes(byte[] value, int offset, int length) {
  311. if (limit - position >= length) {
  312. Array.Copy(value, offset, buffer, position, length);
  313. // We have room in the current buffer.
  314. position += length;
  315. } else {
  316. // Write extends past current buffer. Fill the rest of this buffer and
  317. // flush.
  318. int bytesWritten = limit - position;
  319. Array.Copy(value, offset, buffer, position, bytesWritten);
  320. offset += bytesWritten;
  321. length -= bytesWritten;
  322. position = limit;
  323. RefreshBuffer();
  324. // Now deal with the rest.
  325. // Since we have an output stream, this is our buffer
  326. // and buffer offset == 0
  327. if (length <= limit) {
  328. // Fits in new buffer.
  329. Array.Copy(value, offset, buffer, 0, length);
  330. position = length;
  331. } else {
  332. // Write is very big. Let's do it all at once.
  333. output.Write(value, offset, length);
  334. }
  335. }
  336. }
  337. #endregion
  338. #region Size computations
  339. const int LittleEndian64Size = 8;
  340. const int LittleEndian32Size = 4;
  341. /// <summary>
  342. /// Compute the number of bytes that would be needed to encode a
  343. /// double field, including the tag.
  344. /// </summary>
  345. public static int ComputeDoubleSize(int fieldNumber, double value) {
  346. return ComputeTagSize(fieldNumber) + LittleEndian64Size;
  347. }
  348. /// <summary>
  349. /// Compute the number of bytes that would be needed to encode a
  350. /// float field, including the tag.
  351. /// </summary>
  352. public static int ComputeFloatSize(int fieldNumber, float value) {
  353. return ComputeTagSize(fieldNumber) + LittleEndian32Size;
  354. }
  355. /// <summary>
  356. /// Compute the number of bytes that would be needed to encode a
  357. /// uint64 field, including the tag.
  358. /// </summary>
  359. public static int ComputeUInt64Size(int fieldNumber, ulong value) {
  360. return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
  361. }
  362. /// <summary>
  363. /// Compute the number of bytes that would be needed to encode an
  364. /// int64 field, including the tag.
  365. /// </summary>
  366. public static int ComputeInt64Size(int fieldNumber, long value) {
  367. return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong)value);
  368. }
  369. /// <summary>
  370. /// Compute the number of bytes that would be needed to encode an
  371. /// int32 field, including the tag.
  372. /// </summary>
  373. public static int ComputeInt32Size(int fieldNumber, int value) {
  374. if (value >= 0) {
  375. return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
  376. } else {
  377. // Must sign-extend.
  378. return ComputeTagSize(fieldNumber) + 10;
  379. }
  380. }
  381. /// <summary>
  382. /// Compute the number of bytes that would be needed to encode a
  383. /// fixed64 field, including the tag.
  384. /// </summary>
  385. public static int ComputeFixed64Size(int fieldNumber, long value) {
  386. return ComputeTagSize(fieldNumber) + LittleEndian64Size;
  387. }
  388. /// <summary>
  389. /// Compute the number of bytes that would be needed to encode a
  390. /// fixed32 field, including the tag.
  391. /// </summary>
  392. public static int ComputeFixed32Size(int fieldNumber, int value) {
  393. return ComputeTagSize(fieldNumber) + LittleEndian32Size;
  394. }
  395. /// <summary>
  396. /// Compute the number of bytes that would be needed to encode a
  397. /// bool field, including the tag.
  398. /// </summary>
  399. public static int ComputeBoolSize(int fieldNumber, bool value) {
  400. return ComputeTagSize(fieldNumber) + 1;
  401. }
  402. /// <summary>
  403. /// Compute the number of bytes that would be needed to encode a
  404. /// string field, including the tag.
  405. /// </summary>
  406. public static int ComputeStringSize(int fieldNumber, String value) {
  407. int byteArraySize = Encoding.UTF8.GetByteCount(value);
  408. return ComputeTagSize(fieldNumber) +
  409. ComputeRawVarint32Size((uint)byteArraySize) +
  410. byteArraySize;
  411. }
  412. /// <summary>
  413. /// Compute the number of bytes that would be needed to encode a
  414. /// group field, including the tag.
  415. /// </summary>
  416. public static int ComputeGroupSize(int fieldNumber, IMessage value) {
  417. return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
  418. }
  419. /// <summary>
  420. /// Compute the number of bytes that would be needed to encode a
  421. /// group field represented by an UnknownFieldSet, including the tag.
  422. /// </summary>
  423. public static int ComputeUnknownGroupSize(int fieldNumber,
  424. UnknownFieldSet value) {
  425. return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
  426. }
  427. /// <summary>
  428. /// Compute the number of bytes that would be needed to encode an
  429. /// embedded message field, including the tag.
  430. /// </summary>
  431. public static int ComputeMessageSize(int fieldNumber, IMessage value) {
  432. int size = value.SerializedSize;
  433. return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size;
  434. }
  435. /// <summary>
  436. /// Compute the number of bytes that would be needed to encode a
  437. /// bytes field, including the tag.
  438. /// </summary>
  439. public static int ComputeBytesSize(int fieldNumber, ByteString value) {
  440. return ComputeTagSize(fieldNumber) +
  441. ComputeRawVarint32Size((uint)value.Length) +
  442. value.Length;
  443. }
  444. /// <summary>
  445. /// Compute the number of bytes that would be needed to encode a
  446. /// uint32 field, including the tag.
  447. /// </summary>
  448. public static int ComputeUInt32Size(int fieldNumber, uint value) {
  449. return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
  450. }
  451. /// <summary>
  452. /// Compute the number of bytes that would be needed to encode a
  453. /// enum field, including the tag. The caller is responsible for
  454. /// converting the enum value to its numeric value.
  455. /// </summary>
  456. public static int ComputeEnumSize(int fieldNumber, int value) {
  457. return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
  458. }
  459. /// <summary>
  460. /// Compute the number of bytes that would be needed to encode an
  461. /// sfixed32 field, including the tag.
  462. /// </summary>
  463. public static int ComputeSFixed32Size(int fieldNumber, int value) {
  464. return ComputeTagSize(fieldNumber) + LittleEndian32Size;
  465. }
  466. /// <summary>
  467. /// Compute the number of bytes that would be needed to encode an
  468. /// sfixed64 field, including the tag.
  469. /// </summary>
  470. public static int ComputeSFixed64Size(int fieldNumber, long value) {
  471. return ComputeTagSize(fieldNumber) + LittleEndian64Size;
  472. }
  473. /// <summary>
  474. /// Compute the number of bytes that would be needed to encode an
  475. /// sint32 field, including the tag.
  476. /// </summary>
  477. public static int ComputeSInt32Size(int fieldNumber, int value) {
  478. return ComputeTagSize(fieldNumber) +
  479. ComputeRawVarint32Size(EncodeZigZag32(value));
  480. }
  481. /// <summary>
  482. /// Compute the number of bytes that would be needed to encode an
  483. /// sint64 field, including the tag.
  484. /// </summary>
  485. public static int ComputeSInt64Size(int fieldNumber, long value) {
  486. return ComputeTagSize(fieldNumber) +
  487. ComputeRawVarint64Size(EncodeZigZag64(value));
  488. }
  489. /*
  490. * Compute the number of bytes that would be needed to encode a
  491. * MessageSet extension to the stream. For historical reasons,
  492. * the wire format differs from normal fields.
  493. */
  494. /// <summary>
  495. /// Compute the number of bytes that would be needed to encode a
  496. /// MessageSet extension to the stream. For historical reasons,
  497. /// the wire format differs from normal fields.
  498. /// </summary>
  499. public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) {
  500. return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
  501. ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
  502. ComputeMessageSize(WireFormat.MessageSetField.Message, value);
  503. }
  504. /// <summary>
  505. /// Compute the number of bytes that would be needed to encode an
  506. /// unparsed MessageSet extension field to the stream. For
  507. /// historical reasons, the wire format differs from normal fields.
  508. /// </summary>
  509. public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value) {
  510. return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
  511. ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
  512. ComputeBytesSize(WireFormat.MessageSetField.Message, value);
  513. }
  514. /// <summary>
  515. /// Compute the number of bytes that would be needed to encode a varint.
  516. /// </summary>
  517. public static int ComputeRawVarint32Size(uint value) {
  518. if ((value & (0xffffffff << 7)) == 0) return 1;
  519. if ((value & (0xffffffff << 14)) == 0) return 2;
  520. if ((value & (0xffffffff << 21)) == 0) return 3;
  521. if ((value & (0xffffffff << 28)) == 0) return 4;
  522. return 5;
  523. }
  524. /// <summary>
  525. /// Compute the number of bytes that would be needed to encode a varint.
  526. /// </summary>
  527. public static int ComputeRawVarint64Size(ulong value) {
  528. if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
  529. if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
  530. if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
  531. if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
  532. if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
  533. if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
  534. if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
  535. if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
  536. if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
  537. return 10;
  538. }
  539. /*
  540. * Compute the number of bytes that would be needed to encode a
  541. * field of arbitrary type, including tag, to the stream.
  542. *
  543. * @param type The field's type.
  544. * @param number The field's number.
  545. * @param value Object representing the field's value. Must be of the exact
  546. * type which would be returned by
  547. * {@link Message#getField(Descriptors.FieldDescriptor)} for
  548. * this field.
  549. */
  550. public static int ComputeFieldSize(Descriptors.FieldDescriptor.Type fieldType, int fieldNumber, Object value) {
  551. switch (fieldType) {
  552. case Descriptors.FieldDescriptor.Type.Double: return ComputeDoubleSize(fieldNumber, (double)value);
  553. case Descriptors.FieldDescriptor.Type.Float: return ComputeFloatSize(fieldNumber, (float)value);
  554. case Descriptors.FieldDescriptor.Type.Int64: return ComputeInt64Size(fieldNumber, (long)value);
  555. case Descriptors.FieldDescriptor.Type.UInt64: return ComputeUInt64Size(fieldNumber, (ulong)value);
  556. case Descriptors.FieldDescriptor.Type.Int32: return ComputeInt32Size(fieldNumber, (int)value);
  557. case Descriptors.FieldDescriptor.Type.Fixed64: return ComputeFixed64Size(fieldNumber, (long)value);
  558. case Descriptors.FieldDescriptor.Type.Fixed32: return ComputeFixed32Size(fieldNumber, (int)value);
  559. case Descriptors.FieldDescriptor.Type.Bool: return ComputeBoolSize(fieldNumber, (bool)value);
  560. case Descriptors.FieldDescriptor.Type.String: return ComputeStringSize(fieldNumber, (string)value);
  561. case Descriptors.FieldDescriptor.Type.Group: return ComputeGroupSize(fieldNumber, (IMessage)value);
  562. case Descriptors.FieldDescriptor.Type.Message: return ComputeMessageSize(fieldNumber, (IMessage)value);
  563. case Descriptors.FieldDescriptor.Type.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value);
  564. case Descriptors.FieldDescriptor.Type.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value);
  565. case Descriptors.FieldDescriptor.Type.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value);
  566. case Descriptors.FieldDescriptor.Type.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
  567. case Descriptors.FieldDescriptor.Type.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
  568. case Descriptors.FieldDescriptor.Type.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
  569. case Descriptors.FieldDescriptor.Type.Enum: return ComputeEnumSize(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
  570. default:
  571. throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
  572. }
  573. }
  574. /// <summary>
  575. /// Compute the number of bytes that would be needed to encode a tag.
  576. /// </summary>
  577. public static int ComputeTagSize(int fieldNumber) {
  578. return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
  579. }
  580. #endregion
  581. /// <summary>
  582. /// Encode a 32-bit value with ZigZag encoding.
  583. /// </summary>
  584. /// <remarks>
  585. /// ZigZag encodes signed integers into values that can be efficiently
  586. /// encoded with varint. (Otherwise, negative values must be
  587. /// sign-extended to 64 bits to be varint encoded, thus always taking
  588. /// 10 bytes on the wire.)
  589. /// </remarks>
  590. public static uint EncodeZigZag32(int n) {
  591. // Note: the right-shift must be arithmetic
  592. return (uint)((n << 1) ^ (n >> 31));
  593. }
  594. /// <summary>
  595. /// Encode a 64-bit value with ZigZag encoding.
  596. /// </summary>
  597. /// <remarks>
  598. /// ZigZag encodes signed integers into values that can be efficiently
  599. /// encoded with varint. (Otherwise, negative values must be
  600. /// sign-extended to 64 bits to be varint encoded, thus always taking
  601. /// 10 bytes on the wire.)
  602. /// </remarks>
  603. public static ulong EncodeZigZag64(long n) {
  604. return (ulong)((n << 1) ^ (n >> 63));
  605. }
  606. private void RefreshBuffer() {
  607. if (output == null) {
  608. // We're writing to a single buffer.
  609. throw new OutOfSpaceException();
  610. }
  611. // Since we have an output stream, this is our buffer
  612. // and buffer offset == 0
  613. output.Write(buffer, 0, position);
  614. position = 0;
  615. }
  616. /// <summary>
  617. /// Indicates that a CodedOutputStream wrapping a flat byte array
  618. /// ran out of space.
  619. /// </summary>
  620. public class OutOfSpaceException : IOException {
  621. internal OutOfSpaceException()
  622. : base("CodedOutputStream was writing to a flat byte array and ran out of space.") {
  623. }
  624. }
  625. public void Flush() {
  626. if (output != null) {
  627. RefreshBuffer();
  628. }
  629. }
  630. }
  631. }