ProtoReader.cs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436
  1. 
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Text;
  6. using ProtoBuf.Meta;
  7. namespace ProtoBuf
  8. {
  9. /// <summary>
  10. /// A stateful reader, used to read a protobuf stream. Typical usage would be (sequentially) to call
  11. /// ReadFieldHeader and (after matching the field) an appropriate Read* method.
  12. /// </summary>
  13. public sealed class ProtoReader : IDisposable
  14. {
  15. Stream source;
  16. byte[] ioBuffer;
  17. TypeModel model;
  18. int fieldNumber, depth, ioIndex, available;
  19. long position64, blockEnd64, dataRemaining64;
  20. WireType wireType;
  21. bool isFixedLength, internStrings;
  22. private NetObjectCache netCache;
  23. // this is how many outstanding objects do not currently have
  24. // values for the purposes of reference tracking; we'll default
  25. // to just trapping the root object
  26. // note: objects are trapped (the ref and key mapped) via NoteObject
  27. uint trapCount; // uint is so we can use beq/bne more efficiently than bgt
  28. /// <summary>
  29. /// Gets the number of the field being processed.
  30. /// </summary>
  31. public int FieldNumber => fieldNumber;
  32. /// <summary>
  33. /// Indicates the underlying proto serialization format on the wire.
  34. /// </summary>
  35. public WireType WireType => wireType;
  36. /// <summary>
  37. /// Creates a new reader against a stream
  38. /// </summary>
  39. /// <param name="source">The source stream</param>
  40. /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
  41. /// <param name="context">Additional context about this serialization operation</param>
  42. [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
  43. public ProtoReader(Stream source, TypeModel model, SerializationContext context)
  44. {
  45. Init(this, source, model, context, TO_EOF);
  46. }
  47. internal const long TO_EOF = -1;
  48. /// <summary>
  49. /// Gets / sets a flag indicating whether strings should be checked for repetition; if
  50. /// true, any repeated UTF-8 byte sequence will result in the same String instance, rather
  51. /// than a second instance of the same string. Enabled by default. Note that this uses
  52. /// a <i>custom</i> interner - the system-wide string interner is not used.
  53. /// </summary>
  54. public bool InternStrings { get { return internStrings; } set { internStrings = value; } }
  55. /// <summary>
  56. /// Creates a new reader against a stream
  57. /// </summary>
  58. /// <param name="source">The source stream</param>
  59. /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
  60. /// <param name="context">Additional context about this serialization operation</param>
  61. /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
  62. [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
  63. public ProtoReader(Stream source, TypeModel model, SerializationContext context, int length)
  64. {
  65. Init(this, source, model, context, length);
  66. }
  67. /// <summary>
  68. /// Creates a new reader against a stream
  69. /// </summary>
  70. /// <param name="source">The source stream</param>
  71. /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
  72. /// <param name="context">Additional context about this serialization operation</param>
  73. /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
  74. [Obsolete("Please use ProtoReader.Create; this API may be removed in a future version", error: false)]
  75. public ProtoReader(Stream source, TypeModel model, SerializationContext context, long length)
  76. {
  77. Init(this, source, model, context, length);
  78. }
  79. private static void Init(ProtoReader reader, Stream source, TypeModel model, SerializationContext context, long length)
  80. {
  81. if (source == null) throw new ArgumentNullException(nameof(source));
  82. if (!source.CanRead) throw new ArgumentException("Cannot read from stream", nameof(source));
  83. reader.source = source;
  84. reader.ioBuffer = BufferPool.GetBuffer();
  85. reader.model = model;
  86. bool isFixedLength = length >= 0;
  87. reader.isFixedLength = isFixedLength;
  88. reader.dataRemaining64 = isFixedLength ? length : 0;
  89. if (context == null) { context = SerializationContext.Default; }
  90. else { context.Freeze(); }
  91. reader.context = context;
  92. reader.position64 = 0;
  93. reader.available = reader.depth = reader.fieldNumber = reader.ioIndex = 0;
  94. reader.blockEnd64 = long.MaxValue;
  95. reader.internStrings = RuntimeTypeModel.Default.InternStrings;
  96. reader.wireType = WireType.None;
  97. reader.trapCount = 1;
  98. if (reader.netCache == null) reader.netCache = new NetObjectCache();
  99. }
  100. private SerializationContext context;
  101. /// <summary>
  102. /// Addition information about this deserialization operation.
  103. /// </summary>
  104. public SerializationContext Context => context;
  105. /// <summary>
  106. /// Releases resources used by the reader, but importantly <b>does not</b> Dispose the
  107. /// underlying stream; in many typical use-cases the stream is used for different
  108. /// processes, so it is assumed that the consumer will Dispose their stream separately.
  109. /// </summary>
  110. public void Dispose()
  111. {
  112. // importantly, this does **not** own the stream, and does not dispose it
  113. source = null;
  114. model = null;
  115. BufferPool.ReleaseBufferToPool(ref ioBuffer);
  116. if (stringInterner != null)
  117. {
  118. stringInterner.Clear();
  119. stringInterner = null;
  120. }
  121. if (netCache != null) netCache.Clear();
  122. }
  123. internal int TryReadUInt32VariantWithoutMoving(bool trimNegative, out uint value)
  124. {
  125. if (available < 10) Ensure(10, false);
  126. if (available == 0)
  127. {
  128. value = 0;
  129. return 0;
  130. }
  131. int readPos = ioIndex;
  132. value = ioBuffer[readPos++];
  133. if ((value & 0x80) == 0) return 1;
  134. value &= 0x7F;
  135. if (available == 1) throw EoF(this);
  136. uint chunk = ioBuffer[readPos++];
  137. value |= (chunk & 0x7F) << 7;
  138. if ((chunk & 0x80) == 0) return 2;
  139. if (available == 2) throw EoF(this);
  140. chunk = ioBuffer[readPos++];
  141. value |= (chunk & 0x7F) << 14;
  142. if ((chunk & 0x80) == 0) return 3;
  143. if (available == 3) throw EoF(this);
  144. chunk = ioBuffer[readPos++];
  145. value |= (chunk & 0x7F) << 21;
  146. if ((chunk & 0x80) == 0) return 4;
  147. if (available == 4) throw EoF(this);
  148. chunk = ioBuffer[readPos];
  149. value |= chunk << 28; // can only use 4 bits from this chunk
  150. if ((chunk & 0xF0) == 0) return 5;
  151. if (trimNegative // allow for -ve values
  152. && (chunk & 0xF0) == 0xF0
  153. && available >= 10
  154. && ioBuffer[++readPos] == 0xFF
  155. && ioBuffer[++readPos] == 0xFF
  156. && ioBuffer[++readPos] == 0xFF
  157. && ioBuffer[++readPos] == 0xFF
  158. && ioBuffer[++readPos] == 0x01)
  159. {
  160. return 10;
  161. }
  162. throw AddErrorData(new OverflowException(), this);
  163. }
  164. private uint ReadUInt32Variant(bool trimNegative)
  165. {
  166. int read = TryReadUInt32VariantWithoutMoving(trimNegative, out uint value);
  167. if (read > 0)
  168. {
  169. ioIndex += read;
  170. available -= read;
  171. position64 += read;
  172. return value;
  173. }
  174. throw EoF(this);
  175. }
  176. private bool TryReadUInt32Variant(out uint value)
  177. {
  178. int read = TryReadUInt32VariantWithoutMoving(false, out value);
  179. if (read > 0)
  180. {
  181. ioIndex += read;
  182. available -= read;
  183. position64 += read;
  184. return true;
  185. }
  186. return false;
  187. }
  188. /// <summary>
  189. /// Reads an unsigned 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
  190. /// </summary>
  191. public uint ReadUInt32()
  192. {
  193. switch (wireType)
  194. {
  195. case WireType.Variant:
  196. return ReadUInt32Variant(false);
  197. case WireType.Fixed32:
  198. if (available < 4) Ensure(4, true);
  199. position64 += 4;
  200. available -= 4;
  201. return ((uint)ioBuffer[ioIndex++])
  202. | (((uint)ioBuffer[ioIndex++]) << 8)
  203. | (((uint)ioBuffer[ioIndex++]) << 16)
  204. | (((uint)ioBuffer[ioIndex++]) << 24);
  205. case WireType.Fixed64:
  206. ulong val = ReadUInt64();
  207. checked { return (uint)val; }
  208. default:
  209. throw CreateWireTypeException();
  210. }
  211. }
  212. /// <summary>
  213. /// Returns the position of the current reader (note that this is not necessarily the same as the position
  214. /// in the underlying stream, if multiple readers are used on the same stream)
  215. /// </summary>
  216. public int Position { get { return checked((int)position64); } }
  217. /// <summary>
  218. /// Returns the position of the current reader (note that this is not necessarily the same as the position
  219. /// in the underlying stream, if multiple readers are used on the same stream)
  220. /// </summary>
  221. public long LongPosition { get { return position64; } }
  222. internal void Ensure(int count, bool strict)
  223. {
  224. Helpers.DebugAssert(available <= count, "Asking for data without checking first");
  225. if (count > ioBuffer.Length)
  226. {
  227. BufferPool.ResizeAndFlushLeft(ref ioBuffer, count, ioIndex, available);
  228. ioIndex = 0;
  229. }
  230. else if (ioIndex + count >= ioBuffer.Length)
  231. {
  232. // need to shift the buffer data to the left to make space
  233. Buffer.BlockCopy(ioBuffer, ioIndex, ioBuffer, 0, available);
  234. ioIndex = 0;
  235. }
  236. count -= available;
  237. int writePos = ioIndex + available, bytesRead;
  238. int canRead = ioBuffer.Length - writePos;
  239. if (isFixedLength)
  240. { // throttle it if needed
  241. if (dataRemaining64 < canRead) canRead = (int)dataRemaining64;
  242. }
  243. while (count > 0 && canRead > 0 && (bytesRead = source.Read(ioBuffer, writePos, canRead)) > 0)
  244. {
  245. available += bytesRead;
  246. count -= bytesRead;
  247. canRead -= bytesRead;
  248. writePos += bytesRead;
  249. if (isFixedLength) { dataRemaining64 -= bytesRead; }
  250. }
  251. if (strict && count > 0)
  252. {
  253. throw EoF(this);
  254. }
  255. }
  256. /// <summary>
  257. /// Reads a signed 16-bit integer from the stream: Variant, Fixed32, Fixed64, SignedVariant
  258. /// </summary>
  259. public short ReadInt16()
  260. {
  261. checked { return (short)ReadInt32(); }
  262. }
  263. /// <summary>
  264. /// Reads an unsigned 16-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
  265. /// </summary>
  266. public ushort ReadUInt16()
  267. {
  268. checked { return (ushort)ReadUInt32(); }
  269. }
  270. /// <summary>
  271. /// Reads an unsigned 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
  272. /// </summary>
  273. public byte ReadByte()
  274. {
  275. checked { return (byte)ReadUInt32(); }
  276. }
  277. /// <summary>
  278. /// Reads a signed 8-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
  279. /// </summary>
  280. public sbyte ReadSByte()
  281. {
  282. checked { return (sbyte)ReadInt32(); }
  283. }
  284. /// <summary>
  285. /// Reads a signed 32-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
  286. /// </summary>
  287. public int ReadInt32()
  288. {
  289. switch (wireType)
  290. {
  291. case WireType.Variant:
  292. return (int)ReadUInt32Variant(true);
  293. case WireType.Fixed32:
  294. if (available < 4) Ensure(4, true);
  295. position64 += 4;
  296. available -= 4;
  297. return ((int)ioBuffer[ioIndex++])
  298. | (((int)ioBuffer[ioIndex++]) << 8)
  299. | (((int)ioBuffer[ioIndex++]) << 16)
  300. | (((int)ioBuffer[ioIndex++]) << 24);
  301. case WireType.Fixed64:
  302. long l = ReadInt64();
  303. checked { return (int)l; }
  304. case WireType.SignedVariant:
  305. return Zag(ReadUInt32Variant(true));
  306. default:
  307. throw CreateWireTypeException();
  308. }
  309. }
  310. private const long Int64Msb = ((long)1) << 63;
  311. private const int Int32Msb = ((int)1) << 31;
  312. private static int Zag(uint ziggedValue)
  313. {
  314. int value = (int)ziggedValue;
  315. return (-(value & 0x01)) ^ ((value >> 1) & ~ProtoReader.Int32Msb);
  316. }
  317. private static long Zag(ulong ziggedValue)
  318. {
  319. long value = (long)ziggedValue;
  320. return (-(value & 0x01L)) ^ ((value >> 1) & ~ProtoReader.Int64Msb);
  321. }
  322. /// <summary>
  323. /// Reads a signed 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
  324. /// </summary>
  325. public long ReadInt64()
  326. {
  327. switch (wireType)
  328. {
  329. case WireType.Variant:
  330. return (long)ReadUInt64Variant();
  331. case WireType.Fixed32:
  332. return ReadInt32();
  333. case WireType.Fixed64:
  334. if (available < 8) Ensure(8, true);
  335. position64 += 8;
  336. available -= 8;
  337. #if NETCOREAPP2_1
  338. var result = System.Buffers.Binary.BinaryPrimitives.ReadInt64LittleEndian(ioBuffer.AsSpan(ioIndex, 8));
  339. ioIndex+= 8;
  340. return result;
  341. #else
  342. return ((long)ioBuffer[ioIndex++])
  343. | (((long)ioBuffer[ioIndex++]) << 8)
  344. | (((long)ioBuffer[ioIndex++]) << 16)
  345. | (((long)ioBuffer[ioIndex++]) << 24)
  346. | (((long)ioBuffer[ioIndex++]) << 32)
  347. | (((long)ioBuffer[ioIndex++]) << 40)
  348. | (((long)ioBuffer[ioIndex++]) << 48)
  349. | (((long)ioBuffer[ioIndex++]) << 56);
  350. #endif
  351. case WireType.SignedVariant:
  352. return Zag(ReadUInt64Variant());
  353. default:
  354. throw CreateWireTypeException();
  355. }
  356. }
  357. private int TryReadUInt64VariantWithoutMoving(out ulong value)
  358. {
  359. if (available < 10) Ensure(10, false);
  360. if (available == 0)
  361. {
  362. value = 0;
  363. return 0;
  364. }
  365. int readPos = ioIndex;
  366. value = ioBuffer[readPos++];
  367. if ((value & 0x80) == 0) return 1;
  368. value &= 0x7F;
  369. if (available == 1) throw EoF(this);
  370. ulong chunk = ioBuffer[readPos++];
  371. value |= (chunk & 0x7F) << 7;
  372. if ((chunk & 0x80) == 0) return 2;
  373. if (available == 2) throw EoF(this);
  374. chunk = ioBuffer[readPos++];
  375. value |= (chunk & 0x7F) << 14;
  376. if ((chunk & 0x80) == 0) return 3;
  377. if (available == 3) throw EoF(this);
  378. chunk = ioBuffer[readPos++];
  379. value |= (chunk & 0x7F) << 21;
  380. if ((chunk & 0x80) == 0) return 4;
  381. if (available == 4) throw EoF(this);
  382. chunk = ioBuffer[readPos++];
  383. value |= (chunk & 0x7F) << 28;
  384. if ((chunk & 0x80) == 0) return 5;
  385. if (available == 5) throw EoF(this);
  386. chunk = ioBuffer[readPos++];
  387. value |= (chunk & 0x7F) << 35;
  388. if ((chunk & 0x80) == 0) return 6;
  389. if (available == 6) throw EoF(this);
  390. chunk = ioBuffer[readPos++];
  391. value |= (chunk & 0x7F) << 42;
  392. if ((chunk & 0x80) == 0) return 7;
  393. if (available == 7) throw EoF(this);
  394. chunk = ioBuffer[readPos++];
  395. value |= (chunk & 0x7F) << 49;
  396. if ((chunk & 0x80) == 0) return 8;
  397. if (available == 8) throw EoF(this);
  398. chunk = ioBuffer[readPos++];
  399. value |= (chunk & 0x7F) << 56;
  400. if ((chunk & 0x80) == 0) return 9;
  401. if (available == 9) throw EoF(this);
  402. chunk = ioBuffer[readPos];
  403. value |= chunk << 63; // can only use 1 bit from this chunk
  404. if ((chunk & ~(ulong)0x01) != 0) throw AddErrorData(new OverflowException(), this);
  405. return 10;
  406. }
  407. private ulong ReadUInt64Variant()
  408. {
  409. int read = TryReadUInt64VariantWithoutMoving(out ulong value);
  410. if (read > 0)
  411. {
  412. ioIndex += read;
  413. available -= read;
  414. position64 += read;
  415. return value;
  416. }
  417. throw EoF(this);
  418. }
  419. private Dictionary<string, string> stringInterner;
  420. private string Intern(string value)
  421. {
  422. if (value == null) return null;
  423. if (value.Length == 0) return "";
  424. if (stringInterner == null)
  425. {
  426. stringInterner = new Dictionary<string, string>
  427. {
  428. { value, value }
  429. };
  430. }
  431. else if (stringInterner.TryGetValue(value, out string found))
  432. {
  433. value = found;
  434. }
  435. else
  436. {
  437. stringInterner.Add(value, value);
  438. }
  439. return value;
  440. }
  441. #if COREFX
  442. static readonly Encoding encoding = Encoding.UTF8;
  443. #else
  444. static readonly UTF8Encoding encoding = new UTF8Encoding();
  445. #endif
  446. /// <summary>
  447. /// Reads a string from the stream (using UTF8); supported wire-types: String
  448. /// </summary>
  449. public string ReadString()
  450. {
  451. if (wireType == WireType.String)
  452. {
  453. int bytes = (int)ReadUInt32Variant(false);
  454. if (bytes == 0) return "";
  455. if (available < bytes) Ensure(bytes, true);
  456. string s = encoding.GetString(ioBuffer, ioIndex, bytes);
  457. if (internStrings) { s = Intern(s); }
  458. available -= bytes;
  459. position64 += bytes;
  460. ioIndex += bytes;
  461. return s;
  462. }
  463. throw CreateWireTypeException();
  464. }
  465. /// <summary>
  466. /// Throws an exception indication that the given value cannot be mapped to an enum.
  467. /// </summary>
  468. public void ThrowEnumException(Type type, int value)
  469. {
  470. string desc = type == null ? "<null>" : type.FullName;
  471. throw AddErrorData(new ProtoException("No " + desc + " enum is mapped to the wire-value " + value.ToString()), this);
  472. }
  473. private Exception CreateWireTypeException()
  474. {
  475. return CreateException("Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354");
  476. }
  477. private Exception CreateException(string message)
  478. {
  479. return AddErrorData(new ProtoException(message), this);
  480. }
  481. /// <summary>
  482. /// Reads a double-precision number from the stream; supported wire-types: Fixed32, Fixed64
  483. /// </summary>
  484. public
  485. #if !FEAT_SAFE
  486. unsafe
  487. #endif
  488. double ReadDouble()
  489. {
  490. switch (wireType)
  491. {
  492. case WireType.Fixed32:
  493. return ReadSingle();
  494. case WireType.Fixed64:
  495. long value = ReadInt64();
  496. #if FEAT_SAFE
  497. return BitConverter.ToDouble(BitConverter.GetBytes(value), 0);
  498. #else
  499. return *(double*)&value;
  500. #endif
  501. default:
  502. throw CreateWireTypeException();
  503. }
  504. }
  505. /// <summary>
  506. /// Reads (merges) a sub-message from the stream, internally calling StartSubItem and EndSubItem, and (in between)
  507. /// parsing the message in accordance with the model associated with the reader
  508. /// </summary>
  509. public static object ReadObject(object value, int key, ProtoReader reader)
  510. {
  511. return ReadTypedObject(value, key, reader, null);
  512. }
  513. internal static object ReadTypedObject(object value, int key, ProtoReader reader, Type type)
  514. {
  515. if (reader.model == null)
  516. {
  517. throw AddErrorData(new InvalidOperationException("Cannot deserialize sub-objects unless a model is provided"), reader);
  518. }
  519. SubItemToken token = ProtoReader.StartSubItem(reader);
  520. if (key >= 0)
  521. {
  522. value = reader.model.Deserialize(key, value, reader);
  523. }
  524. else if (type != null && reader.model.TryDeserializeAuxiliaryType(reader, DataFormat.Default, Serializer.ListItemTag, type, ref value, true, false, true, false, null))
  525. {
  526. // ok
  527. }
  528. else
  529. {
  530. TypeModel.ThrowUnexpectedType(type);
  531. }
  532. ProtoReader.EndSubItem(token, reader);
  533. return value;
  534. }
  535. /// <summary>
  536. /// Makes the end of consuming a nested message in the stream; the stream must be either at the correct EndGroup
  537. /// marker, or all fields of the sub-message must have been consumed (in either case, this means ReadFieldHeader
  538. /// should return zero)
  539. /// </summary>
  540. public static void EndSubItem(SubItemToken token, ProtoReader reader)
  541. {
  542. if (reader == null) throw new ArgumentNullException("reader");
  543. long value64 = token.value64;
  544. switch (reader.wireType)
  545. {
  546. case WireType.EndGroup:
  547. if (value64 >= 0) throw AddErrorData(new ArgumentException("token"), reader);
  548. if (-(int)value64 != reader.fieldNumber) throw reader.CreateException("Wrong group was ended"); // wrong group ended!
  549. reader.wireType = WireType.None; // this releases ReadFieldHeader
  550. reader.depth--;
  551. break;
  552. // case WireType.None: // TODO reinstate once reads reset the wire-type
  553. default:
  554. if (value64 < reader.position64) throw reader.CreateException($"Sub-message not read entirely; expected {value64}, was {reader.position64}");
  555. if (reader.blockEnd64 != reader.position64 && reader.blockEnd64 != long.MaxValue)
  556. {
  557. throw reader.CreateException("Sub-message not read correctly");
  558. }
  559. reader.blockEnd64 = value64;
  560. reader.depth--;
  561. break;
  562. /*default:
  563. throw reader.BorkedIt(); */
  564. }
  565. }
  566. /// <summary>
  567. /// Begins consuming a nested message in the stream; supported wire-types: StartGroup, String
  568. /// </summary>
  569. /// <remarks>The token returned must be help and used when callining EndSubItem</remarks>
  570. public static SubItemToken StartSubItem(ProtoReader reader)
  571. {
  572. if (reader == null) throw new ArgumentNullException("reader");
  573. switch (reader.wireType)
  574. {
  575. case WireType.StartGroup:
  576. reader.wireType = WireType.None; // to prevent glitches from double-calling
  577. reader.depth++;
  578. return new SubItemToken((long)(-reader.fieldNumber));
  579. case WireType.String:
  580. long len = (long)reader.ReadUInt64Variant();
  581. if (len < 0) throw AddErrorData(new InvalidOperationException(), reader);
  582. long lastEnd = reader.blockEnd64;
  583. reader.blockEnd64 = reader.position64 + len;
  584. reader.depth++;
  585. return new SubItemToken(lastEnd);
  586. default:
  587. throw reader.CreateWireTypeException(); // throws
  588. }
  589. }
  590. /// <summary>
  591. /// Reads a field header from the stream, setting the wire-type and retuning the field number. If no
  592. /// more fields are available, then 0 is returned. This methods respects sub-messages.
  593. /// </summary>
  594. public int ReadFieldHeader()
  595. {
  596. // at the end of a group the caller must call EndSubItem to release the
  597. // reader (which moves the status to Error, since ReadFieldHeader must
  598. // then be called)
  599. if (blockEnd64 <= position64 || wireType == WireType.EndGroup) { return 0; }
  600. if (TryReadUInt32Variant(out uint tag) && tag != 0)
  601. {
  602. wireType = (WireType)(tag & 7);
  603. fieldNumber = (int)(tag >> 3);
  604. if (fieldNumber < 1) throw new ProtoException("Invalid field in source data: " + fieldNumber.ToString());
  605. }
  606. else
  607. {
  608. wireType = WireType.None;
  609. fieldNumber = 0;
  610. }
  611. if (wireType == ProtoBuf.WireType.EndGroup)
  612. {
  613. if (depth > 0) return 0; // spoof an end, but note we still set the field-number
  614. throw new ProtoException("Unexpected end-group in source data; this usually means the source data is corrupt");
  615. }
  616. return fieldNumber;
  617. }
  618. /// <summary>
  619. /// Looks ahead to see whether the next field in the stream is what we expect
  620. /// (typically; what we've just finished reading - for example ot read successive list items)
  621. /// </summary>
  622. public bool TryReadFieldHeader(int field)
  623. {
  624. // check for virtual end of stream
  625. if (blockEnd64 <= position64 || wireType == WireType.EndGroup) { return false; }
  626. int read = TryReadUInt32VariantWithoutMoving(false, out uint tag);
  627. WireType tmpWireType; // need to catch this to exclude (early) any "end group" tokens
  628. if (read > 0 && ((int)tag >> 3) == field
  629. && (tmpWireType = (WireType)(tag & 7)) != WireType.EndGroup)
  630. {
  631. wireType = tmpWireType;
  632. fieldNumber = field;
  633. position64 += read;
  634. ioIndex += read;
  635. available -= read;
  636. return true;
  637. }
  638. return false;
  639. }
  640. /// <summary>
  641. /// Get the TypeModel associated with this reader
  642. /// </summary>
  643. public TypeModel Model { get { return model; } }
  644. /// <summary>
  645. /// Compares the streams current wire-type to the hinted wire-type, updating the reader if necessary; for example,
  646. /// a Variant may be updated to SignedVariant. If the hinted wire-type is unrelated then no change is made.
  647. /// </summary>
  648. public void Hint(WireType wireType)
  649. {
  650. if (this.wireType == wireType) { } // fine; everything as we expect
  651. else if (((int)wireType & 7) == (int)this.wireType)
  652. { // the underling type is a match; we're customising it with an extension
  653. this.wireType = wireType;
  654. }
  655. // note no error here; we're OK about using alternative data
  656. }
  657. /// <summary>
  658. /// Verifies that the stream's current wire-type is as expected, or a specialized sub-type (for example,
  659. /// SignedVariant) - in which case the current wire-type is updated. Otherwise an exception is thrown.
  660. /// </summary>
  661. public void Assert(WireType wireType)
  662. {
  663. if (this.wireType == wireType) { } // fine; everything as we expect
  664. else if (((int)wireType & 7) == (int)this.wireType)
  665. { // the underling type is a match; we're customising it with an extension
  666. this.wireType = wireType;
  667. }
  668. else
  669. { // nope; that is *not* what we were expecting!
  670. throw CreateWireTypeException();
  671. }
  672. }
  673. /// <summary>
  674. /// Discards the data for the current field.
  675. /// </summary>
  676. public void SkipField()
  677. {
  678. switch (wireType)
  679. {
  680. case WireType.Fixed32:
  681. if (available < 4) Ensure(4, true);
  682. available -= 4;
  683. ioIndex += 4;
  684. position64 += 4;
  685. return;
  686. case WireType.Fixed64:
  687. if (available < 8) Ensure(8, true);
  688. available -= 8;
  689. ioIndex += 8;
  690. position64 += 8;
  691. return;
  692. case WireType.String:
  693. long len = (long)ReadUInt64Variant();
  694. if (len <= available)
  695. { // just jump it!
  696. available -= (int)len;
  697. ioIndex += (int)len;
  698. position64 += len;
  699. return;
  700. }
  701. // everything remaining in the buffer is garbage
  702. position64 += len; // assumes success, but if it fails we're screwed anyway
  703. len -= available; // discount anything we've got to-hand
  704. ioIndex = available = 0; // note that we have no data in the buffer
  705. if (isFixedLength)
  706. {
  707. if (len > dataRemaining64) throw EoF(this);
  708. // else assume we're going to be OK
  709. dataRemaining64 -= len;
  710. }
  711. ProtoReader.Seek(source, len, ioBuffer);
  712. return;
  713. case WireType.Variant:
  714. case WireType.SignedVariant:
  715. ReadUInt64Variant(); // and drop it
  716. return;
  717. case WireType.StartGroup:
  718. int originalFieldNumber = this.fieldNumber;
  719. depth++; // need to satisfy the sanity-checks in ReadFieldHeader
  720. while (ReadFieldHeader() > 0) { SkipField(); }
  721. depth--;
  722. if (wireType == WireType.EndGroup && fieldNumber == originalFieldNumber)
  723. { // we expect to exit in a similar state to how we entered
  724. wireType = ProtoBuf.WireType.None;
  725. return;
  726. }
  727. throw CreateWireTypeException();
  728. case WireType.None: // treat as explicit errorr
  729. case WireType.EndGroup: // treat as explicit error
  730. default: // treat as implicit error
  731. throw CreateWireTypeException();
  732. }
  733. }
  734. /// <summary>
  735. /// Reads an unsigned 64-bit integer from the stream; supported wire-types: Variant, Fixed32, Fixed64
  736. /// </summary>
  737. public ulong ReadUInt64()
  738. {
  739. switch (wireType)
  740. {
  741. case WireType.Variant:
  742. return ReadUInt64Variant();
  743. case WireType.Fixed32:
  744. return ReadUInt32();
  745. case WireType.Fixed64:
  746. if (available < 8) Ensure(8, true);
  747. position64 += 8;
  748. available -= 8;
  749. return ((ulong)ioBuffer[ioIndex++])
  750. | (((ulong)ioBuffer[ioIndex++]) << 8)
  751. | (((ulong)ioBuffer[ioIndex++]) << 16)
  752. | (((ulong)ioBuffer[ioIndex++]) << 24)
  753. | (((ulong)ioBuffer[ioIndex++]) << 32)
  754. | (((ulong)ioBuffer[ioIndex++]) << 40)
  755. | (((ulong)ioBuffer[ioIndex++]) << 48)
  756. | (((ulong)ioBuffer[ioIndex++]) << 56);
  757. default:
  758. throw CreateWireTypeException();
  759. }
  760. }
  761. /// <summary>
  762. /// Reads a single-precision number from the stream; supported wire-types: Fixed32, Fixed64
  763. /// </summary>
  764. public
  765. #if !FEAT_SAFE
  766. unsafe
  767. #endif
  768. float ReadSingle()
  769. {
  770. switch (wireType)
  771. {
  772. case WireType.Fixed32:
  773. {
  774. int value = ReadInt32();
  775. #if FEAT_SAFE
  776. return BitConverter.ToSingle(BitConverter.GetBytes(value), 0);
  777. #else
  778. return *(float*)&value;
  779. #endif
  780. }
  781. case WireType.Fixed64:
  782. {
  783. double value = ReadDouble();
  784. float f = (float)value;
  785. if (float.IsInfinity(f) && !double.IsInfinity(value))
  786. {
  787. throw AddErrorData(new OverflowException(), this);
  788. }
  789. return f;
  790. }
  791. default:
  792. throw CreateWireTypeException();
  793. }
  794. }
  795. /// <summary>
  796. /// Reads a boolean value from the stream; supported wire-types: Variant, Fixed32, Fixed64
  797. /// </summary>
  798. /// <returns></returns>
  799. public bool ReadBoolean()
  800. {
  801. switch (ReadUInt32())
  802. {
  803. case 0: return false;
  804. case 1: return true;
  805. default: throw CreateException("Unexpected boolean value");
  806. }
  807. }
  808. private static readonly byte[] EmptyBlob = new byte[0];
  809. /// <summary>
  810. /// Reads a byte-sequence from the stream, appending them to an existing byte-sequence (which can be null); supported wire-types: String
  811. /// </summary>
  812. public static byte[] AppendBytes(byte[] value, ProtoReader reader)
  813. {
  814. if (reader == null) throw new ArgumentNullException(nameof(reader));
  815. switch (reader.wireType)
  816. {
  817. case WireType.String:
  818. int len = (int)reader.ReadUInt32Variant(false);
  819. reader.wireType = WireType.None;
  820. if (len == 0) return value ?? EmptyBlob;
  821. int offset;
  822. if (value == null || value.Length == 0)
  823. {
  824. offset = 0;
  825. value = new byte[len];
  826. }
  827. else
  828. {
  829. offset = value.Length;
  830. byte[] tmp = new byte[value.Length + len];
  831. Buffer.BlockCopy(value, 0, tmp, 0, value.Length);
  832. value = tmp;
  833. }
  834. // value is now sized with the final length, and (if necessary)
  835. // contains the old data up to "offset"
  836. reader.position64 += len; // assume success
  837. while (len > reader.available)
  838. {
  839. if (reader.available > 0)
  840. {
  841. // copy what we *do* have
  842. Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, offset, reader.available);
  843. len -= reader.available;
  844. offset += reader.available;
  845. reader.ioIndex = reader.available = 0; // we've drained the buffer
  846. }
  847. // now refill the buffer (without overflowing it)
  848. int count = len > reader.ioBuffer.Length ? reader.ioBuffer.Length : len;
  849. if (count > 0) reader.Ensure(count, true);
  850. }
  851. // at this point, we know that len <= available
  852. if (len > 0)
  853. { // still need data, but we have enough buffered
  854. Buffer.BlockCopy(reader.ioBuffer, reader.ioIndex, value, offset, len);
  855. reader.ioIndex += len;
  856. reader.available -= len;
  857. }
  858. return value;
  859. case WireType.Variant:
  860. return new byte[0];
  861. default:
  862. throw reader.CreateWireTypeException();
  863. }
  864. }
  865. //static byte[] ReadBytes(Stream stream, int length)
  866. //{
  867. // if (stream == null) throw new ArgumentNullException("stream");
  868. // if (length < 0) throw new ArgumentOutOfRangeException("length");
  869. // byte[] buffer = new byte[length];
  870. // int offset = 0, read;
  871. // while (length > 0 && (read = stream.Read(buffer, offset, length)) > 0)
  872. // {
  873. // length -= read;
  874. // }
  875. // if (length > 0) throw EoF(null);
  876. // return buffer;
  877. //}
  878. private static int ReadByteOrThrow(Stream source)
  879. {
  880. int val = source.ReadByte();
  881. if (val < 0) throw EoF(null);
  882. return val;
  883. }
  884. /// <summary>
  885. /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
  886. /// reader to be created.
  887. /// </summary>
  888. public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber)
  889. => ReadLengthPrefix(source, expectHeader, style, out fieldNumber, out int bytesRead);
  890. /// <summary>
  891. /// Reads a little-endian encoded integer. An exception is thrown if the data is not all available.
  892. /// </summary>
  893. public static int DirectReadLittleEndianInt32(Stream source)
  894. {
  895. return ReadByteOrThrow(source)
  896. | (ReadByteOrThrow(source) << 8)
  897. | (ReadByteOrThrow(source) << 16)
  898. | (ReadByteOrThrow(source) << 24);
  899. }
  900. /// <summary>
  901. /// Reads a big-endian encoded integer. An exception is thrown if the data is not all available.
  902. /// </summary>
  903. public static int DirectReadBigEndianInt32(Stream source)
  904. {
  905. return (ReadByteOrThrow(source) << 24)
  906. | (ReadByteOrThrow(source) << 16)
  907. | (ReadByteOrThrow(source) << 8)
  908. | ReadByteOrThrow(source);
  909. }
  910. /// <summary>
  911. /// Reads a varint encoded integer. An exception is thrown if the data is not all available.
  912. /// </summary>
  913. public static int DirectReadVarintInt32(Stream source)
  914. {
  915. int bytes = TryReadUInt64Variant(source, out ulong val);
  916. if (bytes <= 0) throw EoF(null);
  917. return checked((int)val);
  918. }
  919. /// <summary>
  920. /// Reads a string (of a given lenth, in bytes) directly from the source into a pre-existing buffer. An exception is thrown if the data is not all available.
  921. /// </summary>
  922. public static void DirectReadBytes(Stream source, byte[] buffer, int offset, int count)
  923. {
  924. int read;
  925. if (source == null) throw new ArgumentNullException("source");
  926. while (count > 0 && (read = source.Read(buffer, offset, count)) > 0)
  927. {
  928. count -= read;
  929. offset += read;
  930. }
  931. if (count > 0) throw EoF(null);
  932. }
  933. /// <summary>
  934. /// Reads a given number of bytes directly from the source. An exception is thrown if the data is not all available.
  935. /// </summary>
  936. public static byte[] DirectReadBytes(Stream source, int count)
  937. {
  938. byte[] buffer = new byte[count];
  939. DirectReadBytes(source, buffer, 0, count);
  940. return buffer;
  941. }
  942. /// <summary>
  943. /// Reads a string (of a given lenth, in bytes) directly from the source. An exception is thrown if the data is not all available.
  944. /// </summary>
  945. public static string DirectReadString(Stream source, int length)
  946. {
  947. byte[] buffer = new byte[length];
  948. DirectReadBytes(source, buffer, 0, length);
  949. return Encoding.UTF8.GetString(buffer, 0, length);
  950. }
  951. /// <summary>
  952. /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
  953. /// reader to be created.
  954. /// </summary>
  955. public static int ReadLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
  956. {
  957. if (style == PrefixStyle.None)
  958. {
  959. bytesRead = fieldNumber = 0;
  960. return int.MaxValue; // avoid the long.maxvalue causing overflow
  961. }
  962. long len64 = ReadLongLengthPrefix(source, expectHeader, style, out fieldNumber, out bytesRead);
  963. return checked((int)len64);
  964. }
  965. /// <summary>
  966. /// Reads the length-prefix of a message from a stream without buffering additional data, allowing a fixed-length
  967. /// reader to be created.
  968. /// </summary>
  969. public static long ReadLongLengthPrefix(Stream source, bool expectHeader, PrefixStyle style, out int fieldNumber, out int bytesRead)
  970. {
  971. fieldNumber = 0;
  972. switch (style)
  973. {
  974. case PrefixStyle.None:
  975. bytesRead = 0;
  976. return long.MaxValue;
  977. case PrefixStyle.Base128:
  978. ulong val;
  979. int tmpBytesRead;
  980. bytesRead = 0;
  981. if (expectHeader)
  982. {
  983. tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
  984. bytesRead += tmpBytesRead;
  985. if (tmpBytesRead > 0)
  986. {
  987. if ((val & 7) != (uint)WireType.String)
  988. { // got a header, but it isn't a string
  989. throw new InvalidOperationException();
  990. }
  991. fieldNumber = (int)(val >> 3);
  992. tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
  993. bytesRead += tmpBytesRead;
  994. if (bytesRead == 0)
  995. { // got a header, but no length
  996. throw EoF(null);
  997. }
  998. return (long)val;
  999. }
  1000. else
  1001. { // no header
  1002. bytesRead = 0;
  1003. return -1;
  1004. }
  1005. }
  1006. // check for a length
  1007. tmpBytesRead = ProtoReader.TryReadUInt64Variant(source, out val);
  1008. bytesRead += tmpBytesRead;
  1009. return bytesRead < 0 ? -1 : (long)val;
  1010. case PrefixStyle.Fixed32:
  1011. {
  1012. int b = source.ReadByte();
  1013. if (b < 0)
  1014. {
  1015. bytesRead = 0;
  1016. return -1;
  1017. }
  1018. bytesRead = 4;
  1019. return b
  1020. | (ReadByteOrThrow(source) << 8)
  1021. | (ReadByteOrThrow(source) << 16)
  1022. | (ReadByteOrThrow(source) << 24);
  1023. }
  1024. case PrefixStyle.Fixed32BigEndian:
  1025. {
  1026. int b = source.ReadByte();
  1027. if (b < 0)
  1028. {
  1029. bytesRead = 0;
  1030. return -1;
  1031. }
  1032. bytesRead = 4;
  1033. return (b << 24)
  1034. | (ReadByteOrThrow(source) << 16)
  1035. | (ReadByteOrThrow(source) << 8)
  1036. | ReadByteOrThrow(source);
  1037. }
  1038. default:
  1039. throw new ArgumentOutOfRangeException("style");
  1040. }
  1041. }
  1042. /// <returns>The number of bytes consumed; 0 if no data available</returns>
  1043. private static int TryReadUInt64Variant(Stream source, out ulong value)
  1044. {
  1045. value = 0;
  1046. int b = source.ReadByte();
  1047. if (b < 0) { return 0; }
  1048. value = (uint)b;
  1049. if ((value & 0x80) == 0) { return 1; }
  1050. value &= 0x7F;
  1051. int bytesRead = 1, shift = 7;
  1052. while (bytesRead < 9)
  1053. {
  1054. b = source.ReadByte();
  1055. if (b < 0) throw EoF(null);
  1056. value |= ((ulong)b & 0x7F) << shift;
  1057. shift += 7;
  1058. bytesRead++;
  1059. if ((b & 0x80) == 0) return bytesRead;
  1060. }
  1061. b = source.ReadByte();
  1062. if (b < 0) throw EoF(null);
  1063. if ((b & 1) == 0) // only use 1 bit from the last byte
  1064. {
  1065. value |= ((ulong)b & 0x7F) << shift;
  1066. return ++bytesRead;
  1067. }
  1068. throw new OverflowException();
  1069. }
  1070. internal static void Seek(Stream source, long count, byte[] buffer)
  1071. {
  1072. if (source.CanSeek)
  1073. {
  1074. source.Seek(count, SeekOrigin.Current);
  1075. count = 0;
  1076. }
  1077. else if (buffer != null)
  1078. {
  1079. int bytesRead;
  1080. while (count > buffer.Length && (bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
  1081. {
  1082. count -= bytesRead;
  1083. }
  1084. while (count > 0 && (bytesRead = source.Read(buffer, 0, (int)count)) > 0)
  1085. {
  1086. count -= bytesRead;
  1087. }
  1088. }
  1089. else // borrow a buffer
  1090. {
  1091. buffer = BufferPool.GetBuffer();
  1092. try
  1093. {
  1094. int bytesRead;
  1095. while (count > buffer.Length && (bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
  1096. {
  1097. count -= bytesRead;
  1098. }
  1099. while (count > 0 && (bytesRead = source.Read(buffer, 0, (int)count)) > 0)
  1100. {
  1101. count -= bytesRead;
  1102. }
  1103. }
  1104. finally
  1105. {
  1106. BufferPool.ReleaseBufferToPool(ref buffer);
  1107. }
  1108. }
  1109. if (count > 0) throw EoF(null);
  1110. }
  1111. internal static Exception AddErrorData(Exception exception, ProtoReader source)
  1112. {
  1113. #if !CF && !PORTABLE
  1114. if (exception != null && source != null && !exception.Data.Contains("protoSource"))
  1115. {
  1116. exception.Data.Add("protoSource", string.Format("tag={0}; wire-type={1}; offset={2}; depth={3}",
  1117. source.fieldNumber, source.wireType, source.position64, source.depth));
  1118. }
  1119. #endif
  1120. return exception;
  1121. }
  1122. private static Exception EoF(ProtoReader source)
  1123. {
  1124. return AddErrorData(new EndOfStreamException(), source);
  1125. }
  1126. /// <summary>
  1127. /// Copies the current field into the instance as extension data
  1128. /// </summary>
  1129. public void AppendExtensionData(IExtensible instance)
  1130. {
  1131. if (instance == null) throw new ArgumentNullException(nameof(instance));
  1132. IExtension extn = instance.GetExtensionObject(true);
  1133. bool commit = false;
  1134. // unusually we *don't* want "using" here; the "finally" does that, with
  1135. // the extension object being responsible for disposal etc
  1136. Stream dest = extn.BeginAppend();
  1137. try
  1138. {
  1139. //TODO: replace this with stream-based, buffered raw copying
  1140. using (ProtoWriter writer = ProtoWriter.Create(dest, model, null))
  1141. {
  1142. AppendExtensionField(writer);
  1143. writer.Close();
  1144. }
  1145. commit = true;
  1146. }
  1147. finally { extn.EndAppend(dest, commit); }
  1148. }
  1149. private void AppendExtensionField(ProtoWriter writer)
  1150. {
  1151. //TODO: replace this with stream-based, buffered raw copying
  1152. ProtoWriter.WriteFieldHeader(fieldNumber, wireType, writer);
  1153. switch (wireType)
  1154. {
  1155. case WireType.Fixed32:
  1156. ProtoWriter.WriteInt32(ReadInt32(), writer);
  1157. return;
  1158. case WireType.Variant:
  1159. case WireType.SignedVariant:
  1160. case WireType.Fixed64:
  1161. ProtoWriter.WriteInt64(ReadInt64(), writer);
  1162. return;
  1163. case WireType.String:
  1164. ProtoWriter.WriteBytes(AppendBytes(null, this), writer);
  1165. return;
  1166. case WireType.StartGroup:
  1167. SubItemToken readerToken = StartSubItem(this),
  1168. writerToken = ProtoWriter.StartSubItem(null, writer);
  1169. while (ReadFieldHeader() > 0) { AppendExtensionField(writer); }
  1170. EndSubItem(readerToken, this);
  1171. ProtoWriter.EndSubItem(writerToken, writer);
  1172. return;
  1173. case WireType.None: // treat as explicit errorr
  1174. case WireType.EndGroup: // treat as explicit error
  1175. default: // treat as implicit error
  1176. throw CreateWireTypeException();
  1177. }
  1178. }
  1179. /// <summary>
  1180. /// Indicates whether the reader still has data remaining in the current sub-item,
  1181. /// additionally setting the wire-type for the next field if there is more data.
  1182. /// This is used when decoding packed data.
  1183. /// </summary>
  1184. public static bool HasSubValue(ProtoBuf.WireType wireType, ProtoReader source)
  1185. {
  1186. if (source == null) throw new ArgumentNullException("source");
  1187. // check for virtual end of stream
  1188. if (source.blockEnd64 <= source.position64 || wireType == WireType.EndGroup) { return false; }
  1189. source.wireType = wireType;
  1190. return true;
  1191. }
  1192. internal int GetTypeKey(ref Type type)
  1193. {
  1194. return model.GetKey(ref type);
  1195. }
  1196. internal NetObjectCache NetCache => netCache;
  1197. internal Type DeserializeType(string value)
  1198. {
  1199. return TypeModel.DeserializeType(model, value);
  1200. }
  1201. internal void SetRootObject(object value)
  1202. {
  1203. netCache.SetKeyedObject(NetObjectCache.Root, value);
  1204. trapCount--;
  1205. }
  1206. /// <summary>
  1207. /// Utility method, not intended for public use; this helps maintain the root object is complex scenarios
  1208. /// </summary>
  1209. public static void NoteObject(object value, ProtoReader reader)
  1210. {
  1211. if (reader == null) throw new ArgumentNullException("reader");
  1212. if (reader.trapCount != 0)
  1213. {
  1214. reader.netCache.RegisterTrappedObject(value);
  1215. reader.trapCount--;
  1216. }
  1217. }
  1218. /// <summary>
  1219. /// Reads a Type from the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String
  1220. /// </summary>
  1221. public Type ReadType()
  1222. {
  1223. return TypeModel.DeserializeType(model, ReadString());
  1224. }
  1225. internal void TrapNextObject(int newObjectKey)
  1226. {
  1227. trapCount++;
  1228. netCache.SetKeyedObject(newObjectKey, null); // use null as a temp
  1229. }
  1230. internal void CheckFullyConsumed()
  1231. {
  1232. if (isFixedLength)
  1233. {
  1234. if (dataRemaining64 != 0) throw new ProtoException("Incorrect number of bytes consumed");
  1235. }
  1236. else
  1237. {
  1238. if (available != 0) throw new ProtoException("Unconsumed data left in the buffer; this suggests corrupt input");
  1239. }
  1240. }
  1241. /// <summary>
  1242. /// Merge two objects using the details from the current reader; this is used to change the type
  1243. /// of objects when an inheritance relationship is discovered later than usual during deserilazation.
  1244. /// </summary>
  1245. public static object Merge(ProtoReader parent, object from, object to)
  1246. {
  1247. if (parent == null) throw new ArgumentNullException("parent");
  1248. TypeModel model = parent.Model;
  1249. SerializationContext ctx = parent.Context;
  1250. if (model == null) throw new InvalidOperationException("Types cannot be merged unless a type-model has been specified");
  1251. using (var ms = new MemoryStream())
  1252. {
  1253. model.Serialize(ms, from, ctx);
  1254. ms.Position = 0;
  1255. return model.Deserialize(ms, to, null);
  1256. }
  1257. }
  1258. #region RECYCLER
  1259. internal static ProtoReader Create(Stream source, TypeModel model, SerializationContext context, int len)
  1260. => Create(source, model, context, (long)len);
  1261. /// <summary>
  1262. /// Creates a new reader against a stream
  1263. /// </summary>
  1264. /// <param name="source">The source stream</param>
  1265. /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to deserialize sub-objects</param>
  1266. /// <param name="context">Additional context about this serialization operation</param>
  1267. /// <param name="length">The number of bytes to read, or -1 to read until the end of the stream</param>
  1268. public static ProtoReader Create(Stream source, TypeModel model, SerializationContext context = null, long length = TO_EOF)
  1269. {
  1270. ProtoReader reader = GetRecycled();
  1271. if (reader == null)
  1272. {
  1273. #pragma warning disable CS0618
  1274. return new ProtoReader(source, model, context, length);
  1275. #pragma warning restore CS0618
  1276. }
  1277. Init(reader, source, model, context, length);
  1278. return reader;
  1279. }
  1280. #if !PLAT_NO_THREADSTATIC
  1281. [ThreadStatic]
  1282. private static ProtoReader lastReader;
  1283. private static ProtoReader GetRecycled()
  1284. {
  1285. ProtoReader tmp = lastReader;
  1286. lastReader = null;
  1287. return tmp;
  1288. }
  1289. internal static void Recycle(ProtoReader reader)
  1290. {
  1291. if (reader != null)
  1292. {
  1293. reader.Dispose();
  1294. lastReader = reader;
  1295. }
  1296. }
  1297. #elif !PLAT_NO_INTERLOCKED
  1298. private static object lastReader;
  1299. private static ProtoReader GetRecycled()
  1300. {
  1301. return (ProtoReader)System.Threading.Interlocked.Exchange(ref lastReader, null);
  1302. }
  1303. internal static void Recycle(ProtoReader reader)
  1304. {
  1305. if(reader != null)
  1306. {
  1307. reader.Dispose();
  1308. System.Threading.Interlocked.Exchange(ref lastReader, reader);
  1309. }
  1310. }
  1311. #else
  1312. private static readonly object recycleLock = new object();
  1313. private static ProtoReader lastReader;
  1314. private static ProtoReader GetRecycled()
  1315. {
  1316. lock(recycleLock)
  1317. {
  1318. ProtoReader tmp = lastReader;
  1319. lastReader = null;
  1320. return tmp;
  1321. }
  1322. }
  1323. internal static void Recycle(ProtoReader reader)
  1324. {
  1325. if(reader != null)
  1326. {
  1327. reader.Dispose();
  1328. lock(recycleLock)
  1329. {
  1330. lastReader = reader;
  1331. }
  1332. }
  1333. }
  1334. #endif
  1335. #endregion
  1336. }
  1337. }