ExtensionRegistry.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://github.com/jskeet/dotnet-protobufs/
  4. // Original C++/Java/Python code:
  5. // http://code.google.com/p/protobuf/
  6. //
  7. // Redistribution and use in source and binary forms, with or without
  8. // modification, are permitted provided that the following conditions are
  9. // met:
  10. //
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Google Inc. nor the names of its
  18. // contributors may be used to endorse or promote products derived from
  19. // this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. using System.Collections.Generic;
  33. using Google.ProtocolBuffers.Descriptors;
  34. using System;
  35. namespace Google.ProtocolBuffers {
  36. /// <summary>
  37. /// A table of known extensions, searchable by name or field number. When
  38. /// parsing a protocol message that might have extensions, you must provide
  39. /// an <see cref="ExtensionRegistry"/> in which you have registered any extensions
  40. /// that you want to be able to parse. Otherwise, those extensions will just
  41. /// be treated like unknown fields.
  42. /// </summary>
  43. /// <example>
  44. /// For example, if you had the <c>.proto</c> file:
  45. /// <code>
  46. /// option java_class = "MyProto";
  47. ///
  48. /// message Foo {
  49. /// extensions 1000 to max;
  50. /// }
  51. ///
  52. /// extend Foo {
  53. /// optional int32 bar;
  54. /// }
  55. /// </code>
  56. ///
  57. /// Then you might write code like:
  58. ///
  59. /// <code>
  60. /// ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
  61. /// registry.Add(MyProto.Bar);
  62. /// MyProto.Foo message = MyProto.Foo.ParseFrom(input, registry);
  63. /// </code>
  64. /// </example>
  65. ///
  66. /// <remarks>
  67. /// <para>You might wonder why this is necessary. Two alternatives might come to
  68. /// mind. First, you might imagine a system where generated extensions are
  69. /// automatically registered when their containing classes are loaded. This
  70. /// is a popular technique, but is bad design; among other things, it creates a
  71. /// situation where behavior can change depending on what classes happen to be
  72. /// loaded. It also introduces a security vulnerability, because an
  73. /// unprivileged class could cause its code to be called unexpectedly from a
  74. /// privileged class by registering itself as an extension of the right type.
  75. /// </para>
  76. /// <para>Another option you might consider is lazy parsing: do not parse an
  77. /// extension until it is first requested, at which point the caller must
  78. /// provide a type to use. This introduces a different set of problems. First,
  79. /// it would require a mutex lock any time an extension was accessed, which
  80. /// would be slow. Second, corrupt data would not be detected until first
  81. /// access, at which point it would be much harder to deal with it. Third, it
  82. /// could violate the expectation that message objects are immutable, since the
  83. /// type provided could be any arbitrary message class. An unprivileged user
  84. /// could take advantage of this to inject a mutable object into a message
  85. /// belonging to privileged code and create mischief.</para>
  86. /// </remarks>
  87. public sealed class ExtensionRegistry {
  88. private static readonly ExtensionRegistry empty = new ExtensionRegistry(
  89. new Dictionary<string, ExtensionInfo>(),
  90. new Dictionary<DescriptorIntPair, ExtensionInfo>(),
  91. true);
  92. private readonly IDictionary<string, ExtensionInfo> extensionsByName;
  93. private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
  94. private readonly bool readOnly;
  95. private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName,
  96. IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber,
  97. bool readOnly) {
  98. this.extensionsByName = extensionsByName;
  99. this.extensionsByNumber = extensionsByNumber;
  100. this.readOnly = readOnly;
  101. }
  102. /// <summary>
  103. /// Construct a new, empty instance.
  104. /// </summary>
  105. public static ExtensionRegistry CreateInstance() {
  106. return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(),
  107. new Dictionary<DescriptorIntPair, ExtensionInfo>(), false);
  108. }
  109. /// <summary>
  110. /// Get the unmodifiable singleton empty instance.
  111. /// </summary>
  112. public static ExtensionRegistry Empty {
  113. get { return empty; }
  114. }
  115. public ExtensionRegistry AsReadOnly() {
  116. return new ExtensionRegistry(extensionsByName, extensionsByNumber, true);
  117. }
  118. /// <summary>
  119. /// Finds an extension by fully-qualified field name, in the
  120. /// proto namespace, i.e. result.Descriptor.FullName will match
  121. /// <paramref name="fullName"/> if a match is found. A null
  122. /// reference is returned if the extension can't be found.
  123. /// </summary>
  124. public ExtensionInfo this[string fullName] {
  125. get {
  126. ExtensionInfo ret;
  127. extensionsByName.TryGetValue(fullName, out ret);
  128. return ret;
  129. }
  130. }
  131. /// <summary>
  132. /// Finds an extension by containing type and field number.
  133. /// A null reference is returned if the extension can't be found.
  134. /// </summary>
  135. public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] {
  136. get {
  137. ExtensionInfo ret;
  138. extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret);
  139. return ret;
  140. }
  141. }
  142. /// <summary>
  143. /// Add an extension from a generated file to the registry.
  144. /// </summary>
  145. public void Add<TExtension> (GeneratedExtensionBase<TExtension> extension) {
  146. if (extension.Descriptor.MappedType == MappedType.Message) {
  147. Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance));
  148. } else {
  149. Add(new ExtensionInfo(extension.Descriptor, null));
  150. }
  151. }
  152. /// <summary>
  153. /// Adds a non-message-type extension to the registry by descriptor.
  154. /// </summary>
  155. /// <param name="type"></param>
  156. public void Add(FieldDescriptor type) {
  157. if (type.MappedType == MappedType.Message) {
  158. throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance "
  159. + "when adding an embedded message extension.");
  160. }
  161. Add(new ExtensionInfo(type, null));
  162. }
  163. /// <summary>
  164. /// Adds a message-type-extension to the registry by descriptor.
  165. /// </summary>
  166. /// <param name="type"></param>
  167. /// <param name="defaultInstance"></param>
  168. public void Add(FieldDescriptor type, IMessage defaultInstance) {
  169. if (type.MappedType != MappedType.Message) {
  170. throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a "
  171. + "non-message extension.");
  172. }
  173. Add(new ExtensionInfo(type, defaultInstance));
  174. }
  175. private void Add(ExtensionInfo extension) {
  176. if (readOnly) {
  177. throw new InvalidOperationException("Cannot add entries to a read-only extension registry");
  178. }
  179. if (!extension.Descriptor.IsExtension) {
  180. throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a "
  181. + "regular (non-extension) field.");
  182. }
  183. extensionsByName[extension.Descriptor.FullName] = extension;
  184. extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType,
  185. extension.Descriptor.FieldNumber)] = extension;
  186. FieldDescriptor field = extension.Descriptor;
  187. if (field.ContainingType.Options.MessageSetWireFormat
  188. && field.FieldType == FieldType.Message
  189. && field.IsOptional
  190. && field.ExtensionScope == field.MessageType) {
  191. // This is an extension of a MessageSet type defined within the extension
  192. // type's own scope. For backwards-compatibility, allow it to be looked
  193. // up by type name.
  194. extensionsByName[field.MessageType.FullName] = extension;
  195. }
  196. }
  197. /// <summary>
  198. /// Nested type just used to represent a pair of MessageDescriptor and int, as
  199. /// the key into the "by number" map.
  200. /// </summary>
  201. private struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
  202. readonly MessageDescriptor descriptor;
  203. readonly int number;
  204. internal DescriptorIntPair(MessageDescriptor descriptor, int number) {
  205. this.descriptor = descriptor;
  206. this.number = number;
  207. }
  208. public override int GetHashCode() {
  209. return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
  210. }
  211. public override bool Equals(object obj) {
  212. if (!(obj is DescriptorIntPair)) {
  213. return false;
  214. }
  215. return Equals((DescriptorIntPair)obj);
  216. }
  217. public bool Equals(DescriptorIntPair other) {
  218. return descriptor == other.descriptor && number == other.number;
  219. }
  220. }
  221. }
  222. }