MessageStreamIterator.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Collections;
  5. using System.IO;
  6. using System.Reflection;
  7. namespace Google.ProtocolBuffers {
  8. /// <summary>
  9. /// Iterates over data created using a <see cref="MessageStreamWriter{T}" />.
  10. /// Unlike MessageStreamWriter, this class is not usually constructed directly with
  11. /// a stream; instead it is provided with a way of opening a stream when iteration
  12. /// is started. The stream is closed when the iteration is completed or the enumerator
  13. /// is disposed. (This occurs naturally when using <c>foreach</c>.)
  14. /// </summary>
  15. public class MessageStreamIterator<TMessage> : IEnumerable<TMessage>
  16. where TMessage : IMessage<TMessage> {
  17. private readonly StreamProvider streamProvider;
  18. private readonly ExtensionRegistry extensionRegistry;
  19. /// <summary>
  20. /// Delegate created via reflection trickery (once per type) to create a builder
  21. /// and read a message from a CodedInputStream with it. Note that unlike in Java,
  22. /// there's one static field per constructed type.
  23. /// </summary>
  24. private static readonly Func<CodedInputStream, ExtensionRegistry, TMessage> messageReader = BuildMessageReader();
  25. /// <summary>
  26. /// Any exception (within reason) thrown within messageReader is caught and rethrown in the constructor.
  27. /// This makes life a lot simpler for the caller.
  28. /// </summary>
  29. private static Exception typeInitializationException;
  30. /// <summary>
  31. /// Creates the delegate later used to read messages. This is only called once per type, but to
  32. /// avoid exceptions occurring at confusing times, if this fails it will set typeInitializationException
  33. /// to the appropriate error and return null.
  34. /// </summary>
  35. private static Func<CodedInputStream, ExtensionRegistry, TMessage> BuildMessageReader() {
  36. try {
  37. Type builderType = FindBuilderType();
  38. // Yes, it's redundant to find this again, but it's only the once...
  39. MethodInfo createBuilderMethod = typeof(TMessage).GetMethod("CreateBuilder", Type.EmptyTypes);
  40. Delegate builderBuilder = Delegate.CreateDelegate(
  41. typeof(Func<>).MakeGenericType(builderType), null, createBuilderMethod);
  42. MethodInfo buildMethod = typeof(MessageStreamIterator<TMessage>)
  43. .GetMethod("BuildImpl", BindingFlags.Static | BindingFlags.NonPublic)
  44. .MakeGenericMethod(typeof(TMessage), builderType);
  45. return (Func<CodedInputStream, ExtensionRegistry, TMessage>)Delegate.CreateDelegate(
  46. typeof(Func<CodedInputStream, ExtensionRegistry, TMessage>), builderBuilder, buildMethod);
  47. } catch (ArgumentException e) {
  48. typeInitializationException = e;
  49. } catch (InvalidOperationException e) {
  50. typeInitializationException = e;
  51. } catch (InvalidCastException e) {
  52. // Can't see why this would happen, but best to know about it.
  53. typeInitializationException = e;
  54. }
  55. return null;
  56. }
  57. /// <summary>
  58. /// Works out the builder type for TMessage, or throws an ArgumentException to explain why it can't.
  59. /// This will check
  60. /// </summary>
  61. private static Type FindBuilderType() {
  62. MethodInfo createBuilderMethod = typeof(TMessage).GetMethod("CreateBuilder", Type.EmptyTypes);
  63. if (createBuilderMethod == null) {
  64. throw new ArgumentException("Message type " + typeof(TMessage).FullName + " has no CreateBuilder method.");
  65. }
  66. if (createBuilderMethod.ReturnType == typeof(void)) {
  67. throw new ArgumentException("CreateBuilder method in " + typeof(TMessage).FullName + " has void return type");
  68. }
  69. Type builderType = createBuilderMethod.ReturnType;
  70. Type messageInterface = typeof(IMessage<,>).MakeGenericType(typeof(TMessage), builderType);
  71. Type builderInterface = typeof(IBuilder<,>).MakeGenericType(typeof(TMessage), builderType);
  72. if (Array.IndexOf(typeof(TMessage).GetInterfaces(), messageInterface) == -1) {
  73. throw new ArgumentException("Message type " + typeof(TMessage) + " doesn't implement " + messageInterface.FullName);
  74. }
  75. if (Array.IndexOf(builderType.GetInterfaces(), builderInterface) == -1) {
  76. throw new ArgumentException("Builder type " + typeof(TMessage) + " doesn't implement " + builderInterface.FullName);
  77. }
  78. return builderType;
  79. }
  80. /// <summary>
  81. /// Method we'll use to build messageReader, with the first parameter fixed to TMessage.CreateBuilder. Note that we
  82. /// have to introduce another type parameter (TMessage2) as we can't constrain TMessage for just a single method
  83. /// (and we can't do it at the type level because we don't know TBuilder). However, by constraining TMessage2
  84. /// to not only implement IMessage appropriately but also to derive from TMessage2, we can avoid doing a cast
  85. /// for every message; the implicit reference conversion will be fine. In practice, TMessage2 and TMessage will
  86. /// be the same type when we construct the generic method by reflection.
  87. /// </summary>
  88. private static TMessage BuildImpl<TMessage2, TBuilder>(Func<TBuilder> builderBuilder, CodedInputStream input, ExtensionRegistry registry)
  89. where TBuilder : IBuilder<TMessage2, TBuilder>
  90. where TMessage2 : TMessage, IMessage<TMessage2, TBuilder> {
  91. TBuilder builder = builderBuilder();
  92. input.ReadMessage(builder, registry);
  93. return builder.Build();
  94. }
  95. private static readonly uint ExpectedTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
  96. private MessageStreamIterator(StreamProvider streamProvider, ExtensionRegistry extensionRegistry) {
  97. if (messageReader == null) {
  98. throw typeInitializationException;
  99. }
  100. this.streamProvider = streamProvider;
  101. this.extensionRegistry = extensionRegistry;
  102. }
  103. /// <summary>
  104. /// Creates a new instance which uses the same stream provider as this one,
  105. /// but the specified extension registry.
  106. /// </summary>
  107. public MessageStreamIterator<TMessage> WithExtensionRegistry(ExtensionRegistry newRegistry) {
  108. return new MessageStreamIterator<TMessage>(streamProvider, newRegistry);
  109. }
  110. public static MessageStreamIterator<TMessage> FromFile(string file) {
  111. return new MessageStreamIterator<TMessage>(() => File.OpenRead(file), ExtensionRegistry.Empty);
  112. }
  113. public static MessageStreamIterator<TMessage> FromStreamProvider(StreamProvider streamProvider) {
  114. return new MessageStreamIterator<TMessage>(streamProvider, ExtensionRegistry.Empty);
  115. }
  116. public IEnumerator<TMessage> GetEnumerator() {
  117. using (Stream stream = streamProvider()) {
  118. CodedInputStream input = CodedInputStream.CreateInstance(stream);
  119. uint tag;
  120. while ((tag = input.ReadTag()) != 0) {
  121. if (tag != ExpectedTag) {
  122. throw InvalidProtocolBufferException.InvalidMessageStreamTag();
  123. }
  124. yield return messageReader(input, extensionRegistry);
  125. }
  126. }
  127. }
  128. IEnumerator IEnumerable.GetEnumerator() {
  129. return GetEnumerator();
  130. }
  131. }
  132. }