1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using Google.Protobuf.Collections; 11 using System; 12 13 namespace Google.Protobuf 14 { 15 internal interface IExtensionValue : IEquatable<IExtensionValue>, IDeepCloneable<IExtensionValue> 16 { MergeFrom(ref ParseContext ctx)17 void MergeFrom(ref ParseContext ctx); 18 MergeFrom(IExtensionValue value)19 void MergeFrom(IExtensionValue value); WriteTo(ref WriteContext ctx)20 void WriteTo(ref WriteContext ctx); CalculateSize()21 int CalculateSize(); IsInitialized()22 bool IsInitialized(); GetValue()23 object GetValue(); 24 } 25 26 internal sealed class ExtensionValue<T> : IExtensionValue 27 { 28 private T field; 29 private readonly FieldCodec<T> codec; 30 ExtensionValue(FieldCodec<T> codec)31 internal ExtensionValue(FieldCodec<T> codec) 32 { 33 this.codec = codec; 34 field = codec.DefaultValue; 35 } 36 CalculateSize()37 public int CalculateSize() 38 { 39 return codec.CalculateUnconditionalSizeWithTag(field); 40 } 41 Clone()42 public IExtensionValue Clone() 43 { 44 return new ExtensionValue<T>(codec) 45 { 46 field = field is IDeepCloneable<T> ? (field as IDeepCloneable<T>).Clone() : field 47 }; 48 } 49 Equals(IExtensionValue other)50 public bool Equals(IExtensionValue other) 51 { 52 if (ReferenceEquals(this, other)) 53 return true; 54 55 return other is ExtensionValue<T> 56 && codec.Equals((other as ExtensionValue<T>).codec) 57 && Equals(field, (other as ExtensionValue<T>).field); 58 // we check for equality in the codec since we could have equal field values however the values could be written in different ways 59 } 60 GetHashCode()61 public override int GetHashCode() 62 { 63 unchecked 64 { 65 int hash = 17; 66 hash = hash * 31 + field.GetHashCode(); 67 hash = hash * 31 + codec.GetHashCode(); 68 return hash; 69 } 70 } 71 MergeFrom(ref ParseContext ctx)72 public void MergeFrom(ref ParseContext ctx) 73 { 74 codec.ValueMerger(ref ctx, ref field); 75 } 76 MergeFrom(IExtensionValue value)77 public void MergeFrom(IExtensionValue value) 78 { 79 if (value is ExtensionValue<T>) 80 { 81 var extensionValue = value as ExtensionValue<T>; 82 codec.FieldMerger(ref field, extensionValue.field); 83 } 84 } 85 WriteTo(ref WriteContext ctx)86 public void WriteTo(ref WriteContext ctx) 87 { 88 ctx.WriteTag(codec.Tag); 89 codec.ValueWriter(ref ctx, field); 90 if (codec.EndTag != 0) 91 { 92 ctx.WriteTag(codec.EndTag); 93 } 94 } 95 GetValue()96 public T GetValue() => field; 97 IExtensionValue.GetValue()98 object IExtensionValue.GetValue() => field; 99 SetValue(T value)100 public void SetValue(T value) 101 { 102 field = value; 103 } 104 IsInitialized()105 public bool IsInitialized() 106 { 107 if (field is IMessage) 108 { 109 return (field as IMessage).IsInitialized(); 110 } 111 else 112 { 113 return true; 114 } 115 } 116 } 117 118 internal sealed class RepeatedExtensionValue<T> : IExtensionValue 119 { 120 private RepeatedField<T> field; 121 private readonly FieldCodec<T> codec; 122 RepeatedExtensionValue(FieldCodec<T> codec)123 internal RepeatedExtensionValue(FieldCodec<T> codec) 124 { 125 this.codec = codec; 126 field = new RepeatedField<T>(); 127 } 128 CalculateSize()129 public int CalculateSize() 130 { 131 return field.CalculateSize(codec); 132 } 133 Clone()134 public IExtensionValue Clone() 135 { 136 return new RepeatedExtensionValue<T>(codec) 137 { 138 field = field.Clone() 139 }; 140 } 141 Equals(IExtensionValue other)142 public bool Equals(IExtensionValue other) 143 { 144 if (ReferenceEquals(this, other)) 145 return true; 146 147 return other is RepeatedExtensionValue<T> 148 && field.Equals((other as RepeatedExtensionValue<T>).field) 149 && codec.Equals((other as RepeatedExtensionValue<T>).codec); 150 } 151 GetHashCode()152 public override int GetHashCode() 153 { 154 unchecked 155 { 156 int hash = 17; 157 hash = hash * 31 + field.GetHashCode(); 158 hash = hash * 31 + codec.GetHashCode(); 159 return hash; 160 } 161 } 162 MergeFrom(ref ParseContext ctx)163 public void MergeFrom(ref ParseContext ctx) 164 { 165 field.AddEntriesFrom(ref ctx, codec); 166 } 167 MergeFrom(IExtensionValue value)168 public void MergeFrom(IExtensionValue value) 169 { 170 if (value is RepeatedExtensionValue<T>) 171 { 172 field.Add((value as RepeatedExtensionValue<T>).field); 173 } 174 } 175 WriteTo(ref WriteContext ctx)176 public void WriteTo(ref WriteContext ctx) 177 { 178 field.WriteTo(ref ctx, codec); 179 } 180 GetValue()181 public RepeatedField<T> GetValue() => field; 182 IExtensionValue.GetValue()183 object IExtensionValue.GetValue() => field; 184 IsInitialized()185 public bool IsInitialized() 186 { 187 for (int i = 0; i < field.Count; i++) 188 { 189 var element = field[i]; 190 if (element is IMessage) 191 { 192 if (!(element as IMessage).IsInitialized()) 193 { 194 return false; 195 } 196 } 197 else 198 { 199 break; 200 } 201 } 202 203 return true; 204 } 205 } 206 } 207