TextFormat.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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.Generic;
  36. using System.Globalization;
  37. using System.IO;
  38. using System.Text;
  39. using Google.ProtocolBuffers.Descriptors;
  40. using System.Collections;
  41. namespace Google.ProtocolBuffers {
  42. /// <summary>
  43. /// Provides ASCII text formatting support for messages.
  44. /// TODO(jonskeet): Support for alternative line endings.
  45. /// (Easy to print, via TextGenerator. Not sure about parsing.)
  46. /// </summary>
  47. public static class TextFormat {
  48. /// <summary>
  49. /// Outputs a textual representation of the Protocol Message supplied into
  50. /// the parameter output.
  51. /// </summary>
  52. public static void Print(IMessage message, TextWriter output) {
  53. TextGenerator generator = new TextGenerator(output, "\n");
  54. Print(message, generator);
  55. }
  56. /// <summary>
  57. /// Outputs a textual representation of <paramref name="fields" /> to <paramref name="output"/>.
  58. /// </summary>
  59. public static void Print(UnknownFieldSet fields, TextWriter output) {
  60. TextGenerator generator = new TextGenerator(output, "\n");
  61. PrintUnknownFields(fields, generator);
  62. }
  63. public static string PrintToString(IMessage message) {
  64. StringWriter text = new StringWriter();
  65. Print(message, text);
  66. return text.ToString();
  67. }
  68. public static string PrintToString(UnknownFieldSet fields) {
  69. StringWriter text = new StringWriter();
  70. Print(fields, text);
  71. return text.ToString();
  72. }
  73. private static void Print(IMessage message, TextGenerator generator) {
  74. foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) {
  75. PrintField(entry.Key, entry.Value, generator);
  76. }
  77. PrintUnknownFields(message.UnknownFields, generator);
  78. }
  79. internal static void PrintField(FieldDescriptor field, object value, TextGenerator generator) {
  80. if (field.IsRepeated) {
  81. // Repeated field. Print each element.
  82. foreach (object element in (IEnumerable) value) {
  83. PrintSingleField(field, element, generator);
  84. }
  85. } else {
  86. PrintSingleField(field, value, generator);
  87. }
  88. }
  89. private static void PrintSingleField(FieldDescriptor field, Object value, TextGenerator generator) {
  90. if (field.IsExtension) {
  91. generator.Print("[");
  92. // We special-case MessageSet elements for compatibility with proto1.
  93. if (field.ContainingType.Options.MessageSetWireFormat
  94. && field.FieldType == FieldType.Message
  95. && field.IsOptional
  96. // object equality (TODO(jonskeet): Work out what this comment means!)
  97. && field.ExtensionScope == field.MessageType) {
  98. generator.Print(field.MessageType.FullName);
  99. } else {
  100. generator.Print(field.FullName);
  101. }
  102. generator.Print("]");
  103. } else {
  104. if (field.FieldType == FieldType.Group) {
  105. // Groups must be serialized with their original capitalization.
  106. generator.Print(field.MessageType.Name);
  107. } else {
  108. generator.Print(field.Name);
  109. }
  110. }
  111. if (field.MappedType == MappedType.Message) {
  112. generator.Print(" {\n");
  113. generator.Indent();
  114. } else {
  115. generator.Print(": ");
  116. }
  117. PrintFieldValue(field, value, generator);
  118. if (field.MappedType == MappedType.Message) {
  119. generator.Outdent();
  120. generator.Print("}");
  121. }
  122. generator.Print("\n");
  123. }
  124. private static void PrintFieldValue(FieldDescriptor field, object value, TextGenerator generator) {
  125. switch (field.FieldType) {
  126. case FieldType.Int32:
  127. case FieldType.Int64:
  128. case FieldType.SInt32:
  129. case FieldType.SInt64:
  130. case FieldType.SFixed32:
  131. case FieldType.SFixed64:
  132. case FieldType.Float:
  133. case FieldType.Double:
  134. case FieldType.UInt32:
  135. case FieldType.UInt64:
  136. case FieldType.Fixed32:
  137. case FieldType.Fixed64:
  138. // The simple Object.ToString converts using the current culture.
  139. // We want to always use the invariant culture so it's predictable.
  140. generator.Print(((IConvertible) value).ToString(CultureInfo.InvariantCulture));
  141. break;
  142. case FieldType.Bool:
  143. // Explicitly use the Java true/false
  144. generator.Print((bool) value ? "true" : "false");
  145. break;
  146. case FieldType.String:
  147. generator.Print("\"");
  148. generator.Print(EscapeText((string) value));
  149. generator.Print("\"");
  150. break;
  151. case FieldType.Bytes: {
  152. generator.Print("\"");
  153. generator.Print(EscapeBytes((ByteString) value));
  154. generator.Print("\"");
  155. break;
  156. }
  157. case FieldType.Enum: {
  158. if (value is IEnumLite && !(value is EnumValueDescriptor)) {
  159. throw new NotSupportedException("Lite enumerations are not supported.");
  160. }
  161. generator.Print(((EnumValueDescriptor)value).Name);
  162. break;
  163. }
  164. case FieldType.Message:
  165. case FieldType.Group:
  166. if (value is IMessageLite && !(value is IMessage)) {
  167. throw new NotSupportedException("Lite messages are not supported.");
  168. }
  169. Print((IMessage)value, generator);
  170. break;
  171. }
  172. }
  173. private static void PrintUnknownFields(UnknownFieldSet unknownFields, TextGenerator generator) {
  174. foreach (KeyValuePair<int, UnknownField> entry in unknownFields.FieldDictionary) {
  175. String prefix = entry.Key.ToString() + ": ";
  176. UnknownField field = entry.Value;
  177. foreach (ulong value in field.VarintList) {
  178. generator.Print(prefix);
  179. generator.Print(value.ToString());
  180. generator.Print("\n");
  181. }
  182. foreach (uint value in field.Fixed32List) {
  183. generator.Print(prefix);
  184. generator.Print(string.Format("0x{0:x8}", value));
  185. generator.Print("\n");
  186. }
  187. foreach (ulong value in field.Fixed64List) {
  188. generator.Print(prefix);
  189. generator.Print(string.Format("0x{0:x16}", value));
  190. generator.Print("\n");
  191. }
  192. foreach (ByteString value in field.LengthDelimitedList) {
  193. generator.Print(entry.Key.ToString());
  194. generator.Print(": \"");
  195. generator.Print(EscapeBytes(value));
  196. generator.Print("\"\n");
  197. }
  198. foreach (UnknownFieldSet value in field.GroupList) {
  199. generator.Print(entry.Key.ToString());
  200. generator.Print(" {\n");
  201. generator.Indent();
  202. PrintUnknownFields(value, generator);
  203. generator.Outdent();
  204. generator.Print("}\n");
  205. }
  206. }
  207. }
  208. [CLSCompliant(false)]
  209. public static ulong ParseUInt64(string text) {
  210. return (ulong) ParseInteger(text, false, true);
  211. }
  212. public static long ParseInt64(string text) {
  213. return ParseInteger(text, true, true);
  214. }
  215. [CLSCompliant(false)]
  216. public static uint ParseUInt32(string text) {
  217. return (uint) ParseInteger(text, false, false);
  218. }
  219. public static int ParseInt32(string text) {
  220. return (int) ParseInteger(text, true, false);
  221. }
  222. public static float ParseFloat(string text) {
  223. switch (text) {
  224. case "-inf":
  225. case "-infinity":
  226. case "-inff":
  227. case "-infinityf":
  228. return float.NegativeInfinity;
  229. case "inf":
  230. case "infinity":
  231. case "inff":
  232. case "infinityf":
  233. return float.PositiveInfinity;
  234. case "nan":
  235. case "nanf":
  236. return float.NaN;
  237. default:
  238. return float.Parse(text, CultureInfo.InvariantCulture);
  239. }
  240. }
  241. public static double ParseDouble(string text) {
  242. switch (text) {
  243. case "-inf":
  244. case "-infinity":
  245. return double.NegativeInfinity;
  246. case "inf":
  247. case "infinity":
  248. return double.PositiveInfinity;
  249. case "nan":
  250. return double.NaN;
  251. default:
  252. return double.Parse(text, CultureInfo.InvariantCulture);
  253. }
  254. }
  255. /// <summary>
  256. /// Parses an integer in hex (leading 0x), decimal (no prefix) or octal (leading 0).
  257. /// Only a negative sign is permitted, and it must come before the radix indicator.
  258. /// </summary>
  259. private static long ParseInteger(string text, bool isSigned, bool isLong) {
  260. string original = text;
  261. bool negative = false;
  262. if (text.StartsWith("-")) {
  263. if (!isSigned) {
  264. throw new FormatException("Number must be positive: " + original);
  265. }
  266. negative = true;
  267. text = text.Substring(1);
  268. }
  269. int radix = 10;
  270. if (text.StartsWith("0x")) {
  271. radix = 16;
  272. text = text.Substring(2);
  273. } else if (text.StartsWith("0")) {
  274. radix = 8;
  275. }
  276. ulong result;
  277. try {
  278. // Workaround for https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448
  279. // We should be able to use Convert.ToUInt64 for all cases.
  280. result = radix == 10 ? ulong.Parse(text) : Convert.ToUInt64(text, radix);
  281. } catch (OverflowException) {
  282. // Convert OverflowException to FormatException so there's a single exception type this method can throw.
  283. string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un");
  284. throw new FormatException("Number out of range for " + numberDescription + ": " + original);
  285. }
  286. if (negative) {
  287. ulong max = isLong ? 0x8000000000000000UL : 0x80000000L;
  288. if (result > max) {
  289. string numberDescription = string.Format("{0}-bit signed integer", isLong ? 64 : 32);
  290. throw new FormatException("Number out of range for " + numberDescription + ": " + original);
  291. }
  292. return -((long) result);
  293. } else {
  294. ulong max = isSigned
  295. ? (isLong ? (ulong) long.MaxValue : int.MaxValue)
  296. : (isLong ? ulong.MaxValue : uint.MaxValue);
  297. if (result > max) {
  298. string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un");
  299. throw new FormatException("Number out of range for " + numberDescription + ": " + original);
  300. }
  301. return (long) result;
  302. }
  303. }
  304. /// <summary>
  305. /// Tests a character to see if it's an octal digit.
  306. /// </summary>
  307. private static bool IsOctal(char c) {
  308. return '0' <= c && c <= '7';
  309. }
  310. /// <summary>
  311. /// Tests a character to see if it's a hex digit.
  312. /// </summary>
  313. private static bool IsHex(char c) {
  314. return ('0' <= c && c <= '9') ||
  315. ('a' <= c && c <= 'f') ||
  316. ('A' <= c && c <= 'F');
  317. }
  318. /// <summary>
  319. /// Interprets a character as a digit (in any base up to 36) and returns the
  320. /// numeric value.
  321. /// </summary>
  322. private static int ParseDigit(char c) {
  323. if ('0' <= c && c <= '9') {
  324. return c - '0';
  325. } else if ('a' <= c && c <= 'z') {
  326. return c - 'a' + 10;
  327. } else {
  328. return c - 'A' + 10;
  329. }
  330. }
  331. /// <summary>
  332. /// Unescapes a text string as escaped using <see cref="EscapeText(string)" />.
  333. /// Two-digit hex escapes (starting with "\x" are also recognised.
  334. /// </summary>
  335. public static string UnescapeText(string input) {
  336. return UnescapeBytes(input).ToStringUtf8();
  337. }
  338. /// <summary>
  339. /// Like <see cref="EscapeBytes" /> but escapes a text string.
  340. /// The string is first encoded as UTF-8, then each byte escaped individually.
  341. /// The returned value is guaranteed to be entirely ASCII.
  342. /// </summary>
  343. public static string EscapeText(string input) {
  344. return EscapeBytes(ByteString.CopyFromUtf8(input));
  345. }
  346. /// <summary>
  347. /// Escapes bytes in the format used in protocol buffer text format, which
  348. /// is the same as the format used for C string literals. All bytes
  349. /// that are not printable 7-bit ASCII characters are escaped, as well as
  350. /// backslash, single-quote, and double-quote characters. Characters for
  351. /// which no defined short-hand escape sequence is defined will be escaped
  352. /// using 3-digit octal sequences.
  353. /// The returned value is guaranteed to be entirely ASCII.
  354. /// </summary>
  355. public static String EscapeBytes(ByteString input) {
  356. StringBuilder builder = new StringBuilder(input.Length);
  357. foreach (byte b in input) {
  358. switch (b) {
  359. // C# does not use \a or \v
  360. case 0x07: builder.Append("\\a" ); break;
  361. case (byte)'\b': builder.Append("\\b" ); break;
  362. case (byte)'\f': builder.Append("\\f" ); break;
  363. case (byte)'\n': builder.Append("\\n" ); break;
  364. case (byte)'\r': builder.Append("\\r" ); break;
  365. case (byte)'\t': builder.Append("\\t" ); break;
  366. case 0x0b: builder.Append("\\v" ); break;
  367. case (byte)'\\': builder.Append("\\\\"); break;
  368. case (byte)'\'': builder.Append("\\\'"); break;
  369. case (byte)'"' : builder.Append("\\\""); break;
  370. default:
  371. if (b >= 0x20 && b < 128) {
  372. builder.Append((char) b);
  373. } else {
  374. builder.Append('\\');
  375. builder.Append((char) ('0' + ((b >> 6) & 3)));
  376. builder.Append((char) ('0' + ((b >> 3) & 7)));
  377. builder.Append((char) ('0' + (b & 7)));
  378. }
  379. break;
  380. }
  381. }
  382. return builder.ToString();
  383. }
  384. /// <summary>
  385. /// Performs string unescaping from C style (octal, hex, form feeds, tab etc) into a byte string.
  386. /// </summary>
  387. public static ByteString UnescapeBytes(string input) {
  388. byte[] result = new byte[input.Length];
  389. int pos = 0;
  390. for (int i = 0; i < input.Length; i++) {
  391. char c = input[i];
  392. if (c > 127 || c < 32) {
  393. throw new FormatException("Escaped string must only contain ASCII");
  394. }
  395. if (c != '\\') {
  396. result[pos++] = (byte) c;
  397. continue;
  398. }
  399. if (i + 1 >= input.Length) {
  400. throw new FormatException("Invalid escape sequence: '\\' at end of string.");
  401. }
  402. i++;
  403. c = input[i];
  404. if (c >= '0' && c <= '7') {
  405. // Octal escape.
  406. int code = ParseDigit(c);
  407. if (i + 1 < input.Length && IsOctal(input[i+1])) {
  408. i++;
  409. code = code * 8 + ParseDigit(input[i]);
  410. }
  411. if (i + 1 < input.Length && IsOctal(input[i+1])) {
  412. i++;
  413. code = code * 8 + ParseDigit(input[i]);
  414. }
  415. result[pos++] = (byte) code;
  416. } else {
  417. switch (c) {
  418. case 'a': result[pos++] = 0x07; break;
  419. case 'b': result[pos++] = (byte) '\b'; break;
  420. case 'f': result[pos++] = (byte) '\f'; break;
  421. case 'n': result[pos++] = (byte) '\n'; break;
  422. case 'r': result[pos++] = (byte) '\r'; break;
  423. case 't': result[pos++] = (byte) '\t'; break;
  424. case 'v': result[pos++] = 0x0b; break;
  425. case '\\': result[pos++] = (byte) '\\'; break;
  426. case '\'': result[pos++] = (byte) '\''; break;
  427. case '"': result[pos++] = (byte) '\"'; break;
  428. case 'x':
  429. // hex escape
  430. int code;
  431. if (i + 1 < input.Length && IsHex(input[i+1])) {
  432. i++;
  433. code = ParseDigit(input[i]);
  434. } else {
  435. throw new FormatException("Invalid escape sequence: '\\x' with no digits");
  436. }
  437. if (i + 1 < input.Length && IsHex(input[i+1])) {
  438. ++i;
  439. code = code * 16 + ParseDigit(input[i]);
  440. }
  441. result[pos++] = (byte)code;
  442. break;
  443. default:
  444. throw new FormatException("Invalid escape sequence: '\\" + c + "'");
  445. }
  446. }
  447. }
  448. return ByteString.CopyFrom(result, 0, pos);
  449. }
  450. public static void Merge(string text, IBuilder builder) {
  451. Merge(text, ExtensionRegistry.Empty, builder);
  452. }
  453. public static void Merge(TextReader reader, IBuilder builder) {
  454. Merge(reader, ExtensionRegistry.Empty, builder);
  455. }
  456. public static void Merge(TextReader reader, ExtensionRegistry registry, IBuilder builder) {
  457. Merge(reader.ReadToEnd(), registry, builder);
  458. }
  459. public static void Merge(string text, ExtensionRegistry registry, IBuilder builder) {
  460. TextTokenizer tokenizer = new TextTokenizer(text);
  461. while (!tokenizer.AtEnd) {
  462. MergeField(tokenizer, registry, builder);
  463. }
  464. }
  465. /// <summary>
  466. /// Parses a single field from the specified tokenizer and merges it into
  467. /// the builder.
  468. /// </summary>
  469. private static void MergeField(TextTokenizer tokenizer, ExtensionRegistry extensionRegistry,
  470. IBuilder builder) {
  471. FieldDescriptor field;
  472. MessageDescriptor type = builder.DescriptorForType;
  473. ExtensionInfo extension = null;
  474. if (tokenizer.TryConsume("[")) {
  475. // An extension.
  476. StringBuilder name = new StringBuilder(tokenizer.ConsumeIdentifier());
  477. while (tokenizer.TryConsume(".")) {
  478. name.Append(".");
  479. name.Append(tokenizer.ConsumeIdentifier());
  480. }
  481. extension = extensionRegistry[name.ToString()];
  482. if (extension == null) {
  483. throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" not found in the ExtensionRegistry.");
  484. } else if (extension.Descriptor.ContainingType != type) {
  485. throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" does not extend message type \"" +
  486. type.FullName + "\".");
  487. }
  488. tokenizer.Consume("]");
  489. field = extension.Descriptor;
  490. } else {
  491. String name = tokenizer.ConsumeIdentifier();
  492. field = type.FindDescriptor<FieldDescriptor>(name);
  493. // Group names are expected to be capitalized as they appear in the
  494. // .proto file, which actually matches their type names, not their field
  495. // names.
  496. if (field == null) {
  497. // Explicitly specify the invariant culture so that this code does not break when
  498. // executing in Turkey.
  499. String lowerName = name.ToLower(CultureInfo.InvariantCulture);
  500. field = type.FindDescriptor<FieldDescriptor>(lowerName);
  501. // If the case-insensitive match worked but the field is NOT a group,
  502. // TODO(jonskeet): What? Java comment ends here!
  503. if (field != null && field.FieldType != FieldType.Group) {
  504. field = null;
  505. }
  506. }
  507. // Again, special-case group names as described above.
  508. if (field != null && field.FieldType == FieldType.Group && field.MessageType.Name != name) {
  509. field = null;
  510. }
  511. if (field == null) {
  512. throw tokenizer.CreateFormatExceptionPreviousToken(
  513. "Message type \"" + type.FullName + "\" has no field named \"" + name + "\".");
  514. }
  515. }
  516. object value = null;
  517. if (field.MappedType == MappedType.Message) {
  518. tokenizer.TryConsume(":"); // optional
  519. String endToken;
  520. if (tokenizer.TryConsume("<")) {
  521. endToken = ">";
  522. } else {
  523. tokenizer.Consume("{");
  524. endToken = "}";
  525. }
  526. IBuilder subBuilder;
  527. if (extension == null) {
  528. subBuilder = builder.CreateBuilderForField(field);
  529. } else {
  530. subBuilder = extension.DefaultInstance.WeakCreateBuilderForType() as IBuilder;
  531. if (subBuilder == null)
  532. throw new NotSupportedException("Lite messages are not supported.");
  533. }
  534. while (!tokenizer.TryConsume(endToken)) {
  535. if (tokenizer.AtEnd) {
  536. throw tokenizer.CreateFormatException("Expected \"" + endToken + "\".");
  537. }
  538. MergeField(tokenizer, extensionRegistry, subBuilder);
  539. }
  540. value = subBuilder.WeakBuild();
  541. } else {
  542. tokenizer.Consume(":");
  543. switch (field.FieldType) {
  544. case FieldType.Int32:
  545. case FieldType.SInt32:
  546. case FieldType.SFixed32:
  547. value = tokenizer.ConsumeInt32();
  548. break;
  549. case FieldType.Int64:
  550. case FieldType.SInt64:
  551. case FieldType.SFixed64:
  552. value = tokenizer.ConsumeInt64();
  553. break;
  554. case FieldType.UInt32:
  555. case FieldType.Fixed32:
  556. value = tokenizer.ConsumeUInt32();
  557. break;
  558. case FieldType.UInt64:
  559. case FieldType.Fixed64:
  560. value = tokenizer.ConsumeUInt64();
  561. break;
  562. case FieldType.Float:
  563. value = tokenizer.ConsumeFloat();
  564. break;
  565. case FieldType.Double:
  566. value = tokenizer.ConsumeDouble();
  567. break;
  568. case FieldType.Bool:
  569. value = tokenizer.ConsumeBoolean();
  570. break;
  571. case FieldType.String:
  572. value = tokenizer.ConsumeString();
  573. break;
  574. case FieldType.Bytes:
  575. value = tokenizer.ConsumeByteString();
  576. break;
  577. case FieldType.Enum: {
  578. EnumDescriptor enumType = field.EnumType;
  579. if (tokenizer.LookingAtInteger()) {
  580. int number = tokenizer.ConsumeInt32();
  581. value = enumType.FindValueByNumber(number);
  582. if (value == null) {
  583. throw tokenizer.CreateFormatExceptionPreviousToken(
  584. "Enum type \"" + enumType.FullName +
  585. "\" has no value with number " + number + ".");
  586. }
  587. } else {
  588. String id = tokenizer.ConsumeIdentifier();
  589. value = enumType.FindValueByName(id);
  590. if (value == null) {
  591. throw tokenizer.CreateFormatExceptionPreviousToken(
  592. "Enum type \"" + enumType.FullName +
  593. "\" has no value named \"" + id + "\".");
  594. }
  595. }
  596. break;
  597. }
  598. case FieldType.Message:
  599. case FieldType.Group:
  600. throw new InvalidOperationException("Can't get here.");
  601. }
  602. }
  603. if (field.IsRepeated) {
  604. builder.WeakAddRepeatedField(field, value);
  605. } else {
  606. builder.SetField(field, value);
  607. }
  608. }
  609. }
  610. }