ExtensionRegistry.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc.
  3. // http://code.google.com/p/protobuf/
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. using System.Collections.Generic;
  17. using Google.ProtocolBuffers.Descriptors;
  18. using System;
  19. namespace Google.ProtocolBuffers {
  20. /// <summary>
  21. /// A table of known extensions, searchable by name or field number. When
  22. /// parsing a protocol message that might have extensions, you must provide
  23. /// an <see cref="ExtensionRegistry"/> in which you have registered any extensions
  24. /// that you want to be able to parse. Otherwise, those extensions will just
  25. /// be treated like unknown fields.
  26. /// </summary>
  27. /// <example>
  28. /// For example, if you had the <c>.proto</c> file:
  29. /// <code>
  30. /// option java_class = "MyProto";
  31. ///
  32. /// message Foo {
  33. /// extensions 1000 to max;
  34. /// }
  35. ///
  36. /// extend Foo {
  37. /// optional int32 bar;
  38. /// }
  39. /// </code>
  40. ///
  41. /// Then you might write code like:
  42. ///
  43. /// <code>
  44. /// ExtensionRegistry registry = ExtensionRegistry.CreateInstance();
  45. /// registry.Add(MyProto.Bar);
  46. /// MyProto.Foo message = MyProto.Foo.ParseFrom(input, registry);
  47. /// </code>
  48. /// </example>
  49. ///
  50. /// <remarks>
  51. /// <para>You might wonder why this is necessary. Two alternatives might come to
  52. /// mind. First, you might imagine a system where generated extensions are
  53. /// automatically registered when their containing classes are loaded. This
  54. /// is a popular technique, but is bad design; among other things, it creates a
  55. /// situation where behavior can change depending on what classes happen to be
  56. /// loaded. It also introduces a security vulnerability, because an
  57. /// unprivileged class could cause its code to be called unexpectedly from a
  58. /// privileged class by registering itself as an extension of the right type.
  59. /// </para>
  60. /// <para>Another option you might consider is lazy parsing: do not parse an
  61. /// extension until it is first requested, at which point the caller must
  62. /// provide a type to use. This introduces a different set of problems. First,
  63. /// it would require a mutex lock any time an extension was accessed, which
  64. /// would be slow. Second, corrupt data would not be detected until first
  65. /// access, at which point it would be much harder to deal with it. Third, it
  66. /// could violate the expectation that message objects are immutable, since the
  67. /// type provided could be any arbitrary message class. An unprivileged user
  68. /// could take advantage of this to inject a mutable object into a message
  69. /// belonging to privileged code and create mischief.</para>
  70. /// </remarks>
  71. public sealed class ExtensionRegistry {
  72. private static readonly ExtensionRegistry empty = new ExtensionRegistry(
  73. new Dictionary<string, ExtensionInfo>(),
  74. new Dictionary<DescriptorIntPair, ExtensionInfo>(),
  75. true);
  76. private readonly IDictionary<string, ExtensionInfo> extensionsByName;
  77. private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
  78. private readonly bool readOnly;
  79. private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName,
  80. IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber,
  81. bool readOnly) {
  82. this.extensionsByName = extensionsByName;
  83. this.extensionsByNumber = extensionsByNumber;
  84. this.readOnly = readOnly;
  85. }
  86. /// <summary>
  87. /// Construct a new, empty instance.
  88. /// </summary>
  89. public static ExtensionRegistry CreateInstance() {
  90. return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(),
  91. new Dictionary<DescriptorIntPair, ExtensionInfo>(), false);
  92. }
  93. /// <summary>
  94. /// Get the unmodifiable singleton empty instance.
  95. /// </summary>
  96. public static ExtensionRegistry Empty {
  97. get { return empty; }
  98. }
  99. public ExtensionRegistry AsReadOnly() {
  100. return new ExtensionRegistry(extensionsByName, extensionsByNumber, true);
  101. }
  102. /// <summary>
  103. /// Finds an extension by fully-qualified field name, in the
  104. /// proto namespace, i.e. result.Descriptor.FullName will match
  105. /// <paramref name="fullName"/> if a match is found. A null
  106. /// reference is returned if the extension can't be found.
  107. /// </summary>
  108. public ExtensionInfo this[string fullName] {
  109. get {
  110. ExtensionInfo ret;
  111. extensionsByName.TryGetValue(fullName, out ret);
  112. return ret;
  113. }
  114. }
  115. /// <summary>
  116. /// Finds an extension by containing type and field number.
  117. /// A null reference is returned if the extension can't be found.
  118. /// </summary>
  119. public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] {
  120. get {
  121. ExtensionInfo ret;
  122. extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret);
  123. return ret;
  124. }
  125. }
  126. /// <summary>
  127. /// Add an extension from a generated file to the registry.
  128. /// </summary>
  129. public void Add<TExtension> (GeneratedExtensionBase<TExtension> extension) {
  130. if (extension.Descriptor.MappedType == MappedType.Message) {
  131. Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance));
  132. } else {
  133. Add(new ExtensionInfo(extension.Descriptor, null));
  134. }
  135. }
  136. /// <summary>
  137. /// Adds a non-message-type extension to the registry by descriptor.
  138. /// </summary>
  139. /// <param name="type"></param>
  140. public void Add(FieldDescriptor type) {
  141. if (type.MappedType == MappedType.Message) {
  142. throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance "
  143. + "when adding an embedded message extension.");
  144. }
  145. Add(new ExtensionInfo(type, null));
  146. }
  147. /// <summary>
  148. /// Adds a message-type-extension to the registry by descriptor.
  149. /// </summary>
  150. /// <param name="type"></param>
  151. /// <param name="defaultInstance"></param>
  152. public void Add(FieldDescriptor type, IMessage defaultInstance) {
  153. if (type.MappedType != MappedType.Message) {
  154. throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a "
  155. + "non-message extension.");
  156. }
  157. Add(new ExtensionInfo(type, defaultInstance));
  158. }
  159. private void Add(ExtensionInfo extension) {
  160. if (readOnly) {
  161. throw new InvalidOperationException("Cannot add entries to a read-only extension registry");
  162. }
  163. if (!extension.Descriptor.IsExtension) {
  164. throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a "
  165. + "regular (non-extension) field.");
  166. }
  167. extensionsByName[extension.Descriptor.FullName] = extension;
  168. extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType,
  169. extension.Descriptor.FieldNumber)] = extension;
  170. FieldDescriptor field = extension.Descriptor;
  171. if (field.ContainingType.Options.MessageSetWireFormat
  172. && field.FieldType == FieldType.Message
  173. && field.IsOptional
  174. && field.ExtensionScope == field.MessageType) {
  175. // This is an extension of a MessageSet type defined within the extension
  176. // type's own scope. For backwards-compatibility, allow it to be looked
  177. // up by type name.
  178. extensionsByName[field.MessageType.FullName] = extension;
  179. }
  180. }
  181. /// <summary>
  182. /// Nested type just used to represent a pair of MessageDescriptor and int, as
  183. /// the key into the "by number" map.
  184. /// </summary>
  185. private struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
  186. readonly MessageDescriptor descriptor;
  187. readonly int number;
  188. internal DescriptorIntPair(MessageDescriptor descriptor, int number) {
  189. this.descriptor = descriptor;
  190. this.number = number;
  191. }
  192. public override int GetHashCode() {
  193. return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
  194. }
  195. public override bool Equals(object obj) {
  196. if (!(obj is DescriptorIntPair)) {
  197. return false;
  198. }
  199. return Equals((DescriptorIntPair)obj);
  200. }
  201. public bool Equals(DescriptorIntPair other) {
  202. return descriptor == other.descriptor && number == other.number;
  203. }
  204. }
  205. }
  206. }