MessageStreamIterator.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. /// Helper class to create MessageStreamIterators without explicitly specifying
  10. /// the builder type. The concrete builder type is determined by reflection, based
  11. /// on the message type. The reflection step looks for a <c>CreateBuilder</c> method
  12. /// in the message type which is public and static, and uses the return type of that
  13. /// method as the builder type. This will work for all generated messages, whether
  14. /// optimised for size or speed. It won't work for dynamic messages.
  15. ///
  16. /// TODO(jonskeet): This also won't work for non-public protos :(
  17. /// Pass in delegate to create a builder?
  18. /// </summary>
  19. public static class MessageStreamIterator {
  20. public static IEnumerable<TMessage> FromFile<TMessage>(string file)
  21. where TMessage : IMessage<TMessage> {
  22. return FromStreamProvider<TMessage>(() => File.OpenRead(file));
  23. }
  24. public static IEnumerable<TMessage> FromStreamProvider<TMessage>(StreamProvider streamProvider)
  25. where TMessage : IMessage<TMessage> {
  26. MethodInfo createBuilderMethod = typeof(TMessage).GetMethod("CreateBuilder", Type.EmptyTypes);
  27. if (createBuilderMethod == null) {
  28. throw new ArgumentException("Message type " + typeof(TMessage).FullName + " has no CreateBuilder method.");
  29. }
  30. if (createBuilderMethod.ReturnType == typeof(void)) {
  31. throw new ArgumentException("CreateBuilder method in " + typeof(TMessage).FullName + " has void return type");
  32. }
  33. Type builderType = createBuilderMethod.ReturnType;
  34. if (builderType.GetConstructor(Type.EmptyTypes) == null) {
  35. throw new ArgumentException("Builder type " + builderType.FullName + " has no public parameterless constructor.");
  36. }
  37. Type messageInterface = typeof(IMessage<,>).MakeGenericType(typeof(TMessage), builderType);
  38. Type builderInterface = typeof(IBuilder<,>).MakeGenericType(typeof(TMessage), builderType);
  39. if (Array.IndexOf(typeof (TMessage).GetInterfaces(), messageInterface) == -1) {
  40. throw new ArgumentException("Message type " + typeof(TMessage) + " doesn't implement " + messageInterface.FullName);
  41. }
  42. if (Array.IndexOf(builderType.GetInterfaces(), builderInterface) == -1) {
  43. throw new ArgumentException("Builder type " + typeof(TMessage) + " doesn't implement " + builderInterface.FullName);
  44. }
  45. Type iteratorType = typeof(MessageStreamIterator<,>).MakeGenericType(typeof(TMessage), builderType);
  46. MethodInfo factoryMethod = iteratorType.GetMethod("FromStreamProvider", new Type[] { typeof(StreamProvider) });
  47. return (IEnumerable<TMessage>) factoryMethod.Invoke(null, new object[] { streamProvider });
  48. }
  49. }
  50. /// <summary>
  51. /// Iterates over data created using a <see cref="MessageStreamWriter{T}" />.
  52. /// Unlike MessageStreamWriter, this class is not usually constructed directly with
  53. /// a stream; instead it is provided with a way of opening a stream when iteration
  54. /// is started. The stream is closed when the iteration is completed or the enumerator
  55. /// is disposed. (This occurs naturally when using <c>foreach</c>.)
  56. /// This type is generic in both the message type and the builder type; if only the
  57. /// iteration is required (i.e. just <see cref="IEnumerable{T}" />) then the static
  58. /// generic methods in the nongeneric class are more appropriate.
  59. /// </summary>
  60. public sealed class MessageStreamIterator<TMessage, TBuilder> : IEnumerable<TMessage>
  61. where TMessage : IMessage<TMessage, TBuilder>
  62. where TBuilder : IBuilder<TMessage, TBuilder>, new() {
  63. private readonly StreamProvider streamProvider;
  64. private readonly ExtensionRegistry extensionRegistry;
  65. private static readonly uint ExpectedTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
  66. private MessageStreamIterator(StreamProvider streamProvider, ExtensionRegistry extensionRegistry) {
  67. this.streamProvider = streamProvider;
  68. this.extensionRegistry = extensionRegistry;
  69. }
  70. /// <summary>
  71. /// Creates an instance which opens the specified file when it begins
  72. /// iterating. No extension registry is used when reading messages.
  73. /// </summary>
  74. public static MessageStreamIterator<TMessage, TBuilder> FromFile(string file) {
  75. return new MessageStreamIterator<TMessage, TBuilder>(() => File.OpenRead(file), ExtensionRegistry.Empty);
  76. }
  77. /// <summary>
  78. /// Creates an instance which calls the given delegate when it begins
  79. /// iterating. No extension registry is used when reading messages.
  80. /// </summary>
  81. public static MessageStreamIterator<TMessage, TBuilder> FromStreamProvider(StreamProvider streamProvider) {
  82. return new MessageStreamIterator<TMessage, TBuilder>(streamProvider, ExtensionRegistry.Empty);
  83. }
  84. /// <summary>
  85. /// Creates a new instance which uses the same stream provider as this one,
  86. /// but the specified extension registry.
  87. /// </summary>
  88. public MessageStreamIterator<TMessage, TBuilder> WithExtensionRegistry(ExtensionRegistry newRegistry) {
  89. return new MessageStreamIterator<TMessage, TBuilder>(streamProvider, newRegistry);
  90. }
  91. public IEnumerator<TMessage> GetEnumerator() {
  92. using (Stream stream = streamProvider()) {
  93. CodedInputStream input = CodedInputStream.CreateInstance(stream);
  94. uint tag;
  95. while ((tag = input.ReadTag()) != 0) {
  96. if (tag != ExpectedTag) {
  97. throw InvalidProtocolBufferException.InvalidMessageStreamTag();
  98. }
  99. TBuilder builder = new TBuilder();
  100. input.ReadMessage(builder, extensionRegistry);
  101. yield return builder.Build();
  102. }
  103. }
  104. }
  105. /// <summary>
  106. /// Explicit implementation of nongeneric IEnumerable interface.
  107. /// </summary>
  108. IEnumerator IEnumerable.GetEnumerator() {
  109. return GetEnumerator();
  110. }
  111. }
  112. }