PageRenderTime 24ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/ProtocolBuffers/MessageStreamIterator.cs

https://code.google.com/p/protobuf-csharp-port/
C# | 244 lines | 144 code | 23 blank | 77 comment | 17 complexity | 3d168377184aa0b22d55bca9271d2539 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, GPL-2.0
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2008 Google Inc. All rights reserved.
  4. // http://github.com/jskeet/dotnet-protobufs/
  5. // Original C++/Java/Python code:
  6. // http://code.google.com/p/protobuf/
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions are
  10. // met:
  11. //
  12. // * Redistributions of source code must retain the above copyright
  13. // notice, this list of conditions and the following disclaimer.
  14. // * Redistributions in binary form must reproduce the above
  15. // copyright notice, this list of conditions and the following disclaimer
  16. // in the documentation and/or other materials provided with the
  17. // distribution.
  18. // * Neither the name of Google Inc. nor the names of its
  19. // contributors may be used to endorse or promote products derived from
  20. // this software without specific prior written permission.
  21. //
  22. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. #endregion
  34. using System;
  35. using System.Collections;
  36. using System.Collections.Generic;
  37. using System.IO;
  38. using System.Reflection;
  39. namespace Google.ProtocolBuffers
  40. {
  41. /// <summary>
  42. /// Iterates over data created using a <see cref="MessageStreamWriter{T}" />.
  43. /// Unlike MessageStreamWriter, this class is not usually constructed directly with
  44. /// a stream; instead it is provided with a way of opening a stream when iteration
  45. /// is started. The stream is closed when the iteration is completed or the enumerator
  46. /// is disposed. (This occurs naturally when using <c>foreach</c>.)
  47. /// </summary>
  48. public class MessageStreamIterator<TMessage> : IEnumerable<TMessage>
  49. where TMessage : IMessage<TMessage>
  50. {
  51. private readonly StreamProvider streamProvider;
  52. private readonly ExtensionRegistry extensionRegistry;
  53. private readonly int sizeLimit;
  54. // Type.EmptyTypes isn't present on the compact framework
  55. private static readonly Type[] EmptyTypes = new Type[0];
  56. /// <summary>
  57. /// Delegate created via reflection trickery (once per type) to create a builder
  58. /// and read a message from a CodedInputStream with it. Note that unlike in Java,
  59. /// there's one static field per constructed type.
  60. /// </summary>
  61. private static readonly Func<CodedInputStream, ExtensionRegistry, TMessage> messageReader = BuildMessageReader();
  62. /// <summary>
  63. /// Any exception (within reason) thrown within messageReader is caught and rethrown in the constructor.
  64. /// This makes life a lot simpler for the caller.
  65. /// </summary>
  66. private static Exception typeInitializationException;
  67. /// <summary>
  68. /// Creates the delegate later used to read messages. This is only called once per type, but to
  69. /// avoid exceptions occurring at confusing times, if this fails it will set typeInitializationException
  70. /// to the appropriate error and return null.
  71. /// </summary>
  72. private static Func<CodedInputStream, ExtensionRegistry, TMessage> BuildMessageReader()
  73. {
  74. try
  75. {
  76. Type builderType = FindBuilderType();
  77. // Yes, it's redundant to find this again, but it's only the once...
  78. MethodInfo createBuilderMethod = typeof (TMessage).GetMethod("CreateBuilder", EmptyTypes);
  79. Delegate builderBuilder = Delegate.CreateDelegate(
  80. typeof (Func<>).MakeGenericType(builderType), null, createBuilderMethod);
  81. MethodInfo buildMethod = typeof (MessageStreamIterator<TMessage>)
  82. .GetMethod("BuildImpl", BindingFlags.Static | BindingFlags.NonPublic)
  83. .MakeGenericMethod(typeof (TMessage), builderType);
  84. return (Func<CodedInputStream, ExtensionRegistry, TMessage>) Delegate.CreateDelegate(
  85. typeof (Func<CodedInputStream, ExtensionRegistry, TMessage>), builderBuilder, buildMethod);
  86. }
  87. catch (ArgumentException e)
  88. {
  89. typeInitializationException = e;
  90. }
  91. catch (InvalidOperationException e)
  92. {
  93. typeInitializationException = e;
  94. }
  95. catch (InvalidCastException e)
  96. {
  97. // Can't see why this would happen, but best to know about it.
  98. typeInitializationException = e;
  99. }
  100. return null;
  101. }
  102. /// <summary>
  103. /// Works out the builder type for TMessage, or throws an ArgumentException to explain why it can't.
  104. /// </summary>
  105. private static Type FindBuilderType()
  106. {
  107. MethodInfo createBuilderMethod = typeof (TMessage).GetMethod("CreateBuilder", EmptyTypes);
  108. if (createBuilderMethod == null)
  109. {
  110. throw new ArgumentException("Message type " + typeof (TMessage).FullName +
  111. " has no CreateBuilder method.");
  112. }
  113. if (createBuilderMethod.ReturnType == typeof (void))
  114. {
  115. throw new ArgumentException("CreateBuilder method in " + typeof (TMessage).FullName +
  116. " has void return type");
  117. }
  118. Type builderType = createBuilderMethod.ReturnType;
  119. Type messageInterface = typeof (IMessage<,>).MakeGenericType(typeof (TMessage), builderType);
  120. Type builderInterface = typeof (IBuilder<,>).MakeGenericType(typeof (TMessage), builderType);
  121. if (Array.IndexOf(typeof (TMessage).GetInterfaces(), messageInterface) == -1)
  122. {
  123. throw new ArgumentException("Message type " + typeof (TMessage) + " doesn't implement " +
  124. messageInterface.FullName);
  125. }
  126. if (Array.IndexOf(builderType.GetInterfaces(), builderInterface) == -1)
  127. {
  128. throw new ArgumentException("Builder type " + typeof (TMessage) + " doesn't implement " +
  129. builderInterface.FullName);
  130. }
  131. return builderType;
  132. }
  133. // This is only ever fetched by reflection, so the compiler may
  134. // complain that it's unused
  135. #pragma warning disable 0169
  136. /// <summary>
  137. /// Method we'll use to build messageReader, with the first parameter fixed to TMessage.CreateBuilder. Note that we
  138. /// have to introduce another type parameter (TMessage2) as we can't constrain TMessage for just a single method
  139. /// (and we can't do it at the type level because we don't know TBuilder). However, by constraining TMessage2
  140. /// to not only implement IMessage appropriately but also to derive from TMessage2, we can avoid doing a cast
  141. /// for every message; the implicit reference conversion will be fine. In practice, TMessage2 and TMessage will
  142. /// be the same type when we construct the generic method by reflection.
  143. /// </summary>
  144. private static TMessage BuildImpl<TMessage2, TBuilder>(Func<TBuilder> builderBuilder, CodedInputStream input,
  145. ExtensionRegistry registry)
  146. where TBuilder : IBuilder<TMessage2, TBuilder>
  147. where TMessage2 : TMessage, IMessage<TMessage2, TBuilder>
  148. {
  149. TBuilder builder = builderBuilder();
  150. input.ReadMessage(builder, registry);
  151. return builder.Build();
  152. }
  153. #pragma warning restore 0414
  154. private static readonly uint ExpectedTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
  155. private MessageStreamIterator(StreamProvider streamProvider, ExtensionRegistry extensionRegistry, int sizeLimit)
  156. {
  157. if (messageReader == null)
  158. {
  159. throw typeInitializationException;
  160. }
  161. this.streamProvider = streamProvider;
  162. this.extensionRegistry = extensionRegistry;
  163. this.sizeLimit = sizeLimit;
  164. }
  165. private MessageStreamIterator(StreamProvider streamProvider, ExtensionRegistry extensionRegistry)
  166. : this(streamProvider, extensionRegistry, CodedInputStream.DefaultSizeLimit)
  167. {
  168. }
  169. /// <summary>
  170. /// Creates a new instance which uses the same stream provider as this one,
  171. /// but the specified extension registry.
  172. /// </summary>
  173. public MessageStreamIterator<TMessage> WithExtensionRegistry(ExtensionRegistry newRegistry)
  174. {
  175. return new MessageStreamIterator<TMessage>(streamProvider, newRegistry, sizeLimit);
  176. }
  177. /// <summary>
  178. /// Creates a new instance which uses the same stream provider and extension registry as this one,
  179. /// but with the specified size limit. Note that this must be big enough for the largest message
  180. /// and the tag and size preceding it.
  181. /// </summary>
  182. public MessageStreamIterator<TMessage> WithSizeLimit(int newSizeLimit)
  183. {
  184. return new MessageStreamIterator<TMessage>(streamProvider, extensionRegistry, newSizeLimit);
  185. }
  186. public static MessageStreamIterator<TMessage> FromFile(string file)
  187. {
  188. return new MessageStreamIterator<TMessage>(() => File.OpenRead(file), ExtensionRegistry.Empty);
  189. }
  190. public static MessageStreamIterator<TMessage> FromStreamProvider(StreamProvider streamProvider)
  191. {
  192. return new MessageStreamIterator<TMessage>(streamProvider, ExtensionRegistry.Empty);
  193. }
  194. public IEnumerator<TMessage> GetEnumerator()
  195. {
  196. using (Stream stream = streamProvider())
  197. {
  198. CodedInputStream input = CodedInputStream.CreateInstance(stream);
  199. input.SetSizeLimit(sizeLimit);
  200. uint tag;
  201. string name;
  202. while (input.ReadTag(out tag, out name))
  203. {
  204. if ((tag == 0 && name == "item") || (tag == ExpectedTag))
  205. {
  206. yield return messageReader(input, extensionRegistry);
  207. }
  208. else
  209. {
  210. throw InvalidProtocolBufferException.InvalidMessageStreamTag();
  211. }
  212. input.ResetSizeCounter();
  213. }
  214. }
  215. }
  216. IEnumerator IEnumerable.GetEnumerator()
  217. {
  218. return GetEnumerator();
  219. }
  220. }
  221. }