First checkin with code
							parent
							
								
									732b8f5940
								
							
						
					
					
						commit
						53a7cdd337
					
				|  | @ -0,0 +1 @@ | |||
| .DS_Store | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 68f75ad8b6624e2ca86deea09d90452e | ||||
| timeCreated: 1693367821 | ||||
|  | @ -1,3 +0,0 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0cce6bb95d7a4c62a10114242afa5176 | ||||
| timeCreated: 1693367793 | ||||
|  | @ -0,0 +1,18 @@ | |||
| { | ||||
|     "name": "Protobuf-net.Editor", | ||||
|     "rootNamespace": "", | ||||
|     "references": [ | ||||
|         "GUID:832e7ae06a4304a17a11ca2f7b21373d" | ||||
|     ], | ||||
|     "includePlatforms": [ | ||||
|         "Editor" | ||||
|     ], | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": true, | ||||
|     "overrideReferences": false, | ||||
|     "precompiledReferences": [], | ||||
|     "autoReferenced": true, | ||||
|     "defineConstraints": [], | ||||
|     "versionDefines": [], | ||||
|     "noEngineReferences": false | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b8a90c429cd0f45168d25ef7957cc732 | ||||
| AssemblyDefinitionImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,248 @@ | |||
| // | ||||
| // Proto2CSEditor.cs | ||||
| // | ||||
| // Author: | ||||
| //       JasonXuDeveloper(傑) <jasonxudeveloper@gmail.com> | ||||
| // | ||||
| // Copyright (c) 2020 JEngine | ||||
| // | ||||
| // Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| // of this software and associated documentation files (the "Software"), to deal | ||||
| // in the Software without restriction, including without limitation the rights | ||||
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| // copies of the Software, and to permit persons to whom the Software is | ||||
| // furnished to do so, subject to the following conditions: | ||||
| // | ||||
| // The above copyright notice and this permission notice shall be included in | ||||
| // all copies or substantial portions of the Software. | ||||
| // | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| // THE SOFTWARE. | ||||
| 
 | ||||
| using System.IO; | ||||
| using Google.Protobuf.Reflection; | ||||
| using ProtoBuf.Reflection; | ||||
| using UnityEditor; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace Guru | ||||
| { | ||||
|     internal class Proto2CSEditor : EditorWindow | ||||
|     { | ||||
|         private static Proto2CSEditor win; | ||||
| 
 | ||||
|         //[MenuItem("Tools/Protobuf/Show Generate Window")] | ||||
|         //public static void ShowCSGenerateWindow() | ||||
|         //{ | ||||
|         //    int index = Application.dataPath.LastIndexOf('/'); | ||||
|         //    var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos"; | ||||
|         //    win = GetWindow<Proto2CSEditor>("Proto2CS Generator"); | ||||
|         //    win.folder = EditorUtility.OpenFolderPanel("Please select proto files directory", | ||||
|         //        proto_dir, ""); | ||||
|         //    win.minSize = new Vector2(500, 500); | ||||
|         //    win.Show(); | ||||
|         //} | ||||
| 
 | ||||
|         [MenuItem("Tools/Protobuf/Generate All")] | ||||
|         public static void GenerateAllProtos() | ||||
|         { | ||||
|             int index = Application.dataPath.LastIndexOf('/'); | ||||
|             var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos"; | ||||
|             var file_list = GetAllProtoFiles(proto_dir); | ||||
|             var dest_folder = $"{Application.dataPath}/../Assets/Scripts/NetworkGen"; | ||||
| 
 | ||||
|             if (Directory.Exists(dest_folder)) | ||||
|             { | ||||
|                 Directory.Delete(dest_folder, true); | ||||
| 
 | ||||
|                 // Just in case the system has output_folder still locked for deletion | ||||
|                 while (Directory.Exists(dest_folder)) | ||||
|                 { | ||||
|                     System.Threading.Thread.Sleep(10); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             Directory.CreateDirectory(dest_folder); | ||||
| 
 | ||||
|             Generate(proto_dir, file_list, dest_folder); | ||||
|         } | ||||
| 
 | ||||
|         [MenuItem("Tools/Protobuf/View Proto Files")] | ||||
|         private static void ViewDataPath() | ||||
|         { | ||||
|             int index = Application.dataPath.LastIndexOf('/'); | ||||
|             var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos"; | ||||
| 
 | ||||
|             if (!Directory.Exists(proto_dir)) | ||||
|             { | ||||
|                 Directory.CreateDirectory(proto_dir); | ||||
|             } | ||||
| 
 | ||||
|             EditorUtility.OpenWithDefaultApp(proto_dir); | ||||
|         } | ||||
| 
 | ||||
|         [SerializeField] protected string[] _fileList = new string[0]; | ||||
|         protected string folder; | ||||
|         protected SerializedObject _serializedObject; | ||||
|         protected SerializedProperty _fileListProperty; | ||||
| 
 | ||||
| 
 | ||||
|         protected void OnEnable() | ||||
|         { | ||||
|             //使用当前类初始化 | ||||
|             _serializedObject = new SerializedObject(this); | ||||
|             //获取当前类中可序列话的属性 | ||||
|             _fileListProperty = _serializedObject.FindProperty("_fileList"); | ||||
|         } | ||||
| 
 | ||||
|         protected void OnGUI() | ||||
|         { | ||||
|             //绘制标题 | ||||
|             GUILayout.Space(10); | ||||
|             GUIStyle textStyle = new GUIStyle(); | ||||
|             textStyle.fontSize = 24; | ||||
|             textStyle.normal.textColor = Color.white; | ||||
|             textStyle.alignment = TextAnchor.MiddleCenter; | ||||
|             GUILayout.Label("Proto文件转CS文件", textStyle); | ||||
|             textStyle.fontSize = 18; | ||||
|             GUILayout.Label("Proto file to CS file", textStyle); | ||||
|             GUILayout.Space(10); | ||||
| 
 | ||||
|             /* | ||||
| 			 * 路径 | ||||
| 			 */ | ||||
|             GUILayout.Label("Proto file folder Proto文件路径"); | ||||
|             GUILayout.BeginHorizontal(); | ||||
|             EditorGUI.BeginDisabledGroup(true); | ||||
|             folder = EditorGUILayout.TextField(folder); | ||||
|             EditorGUI.EndDisabledGroup(); | ||||
| 
 | ||||
|             GUILayout.Space(10); | ||||
|             if (GUILayout.Button("Select Path 选择路径", GUILayout.ExpandWidth(false))) | ||||
|             { | ||||
|                 int index = Application.dataPath.LastIndexOf('/'); | ||||
|                 var proto_dir = $"{Application.dataPath.Substring(0, index)}/ServerProtos"; | ||||
| 
 | ||||
|                 folder = EditorUtility.OpenFolderPanel("Select proto files source 请选择proto文件路径", proto_dir, ""); | ||||
|             } | ||||
| 
 | ||||
|             GUILayout.EndHorizontal(); | ||||
| 
 | ||||
|             /* | ||||
| 			 * 文件 | ||||
| 			 */ | ||||
|             GUILayout.Space(10); | ||||
|             GUILayout.Label("Files to convert 需转换文件"); | ||||
|             //更新 | ||||
|             _serializedObject.Update(); | ||||
|             //开始检查是否有修改 | ||||
|             EditorGUI.BeginChangeCheck(); | ||||
|             //显示属性 | ||||
|             EditorGUILayout.PropertyField(_fileListProperty, true); | ||||
| 
 | ||||
|             //结束检查是否有修改 | ||||
|             if (EditorGUI.EndChangeCheck()) | ||||
|             { | ||||
|                 //提交修改 | ||||
|                 _serializedObject.ApplyModifiedProperties(); | ||||
|             } | ||||
| 
 | ||||
|             /* | ||||
| 			 * 按钮 | ||||
| 			 */ | ||||
|             GUILayout.Space(50); | ||||
|             if (GUILayout.Button("Match all files from folder 从文件夹中匹配全部文件")) | ||||
|             { | ||||
|                 _fileList = GetAllProtoFiles(folder); | ||||
|                 _serializedObject.Update(); | ||||
|             } | ||||
| 
 | ||||
|             GUILayout.Space(10); | ||||
|             if (GUILayout.Button("Generate 生成")) | ||||
|             { | ||||
|                 var dest_folder = $"{Application.dataPath}/Gen/Network"; | ||||
|                 //Generate(folder, _fileList, dest_folder); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static string[] GetAllProtoFiles(string path) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(path)) | ||||
|             { | ||||
|                 Debug.LogError($"Folder path is empty!"); | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             var file_list = Directory.GetFiles(path, "*.proto", SearchOption.AllDirectories); | ||||
|             var file_name_list = new string[file_list.Length]; | ||||
| 
 | ||||
|             for (int i = 0; i < file_list.Length; i++) | ||||
|             { | ||||
|                 file_name_list[i] = Path.GetFileName(file_list[i]); | ||||
|             } | ||||
| 
 | ||||
|             return file_name_list; | ||||
|         } | ||||
| 
 | ||||
|         private static void Generate(string inpath, string[] inprotos, string outpath) | ||||
|         { | ||||
|             if (!Directory.Exists(outpath)) | ||||
|             { | ||||
|                 Directory.CreateDirectory(outpath); | ||||
|             } | ||||
| 
 | ||||
|             var set = new FileDescriptorSet(); | ||||
|             set.AddImportPath(inpath); | ||||
|             foreach (var inproto in inprotos) | ||||
|             { | ||||
|                 var s = inproto; | ||||
|                 if (!inproto.Contains(".proto")) | ||||
|                 { | ||||
|                     s += ".proto"; | ||||
|                 } | ||||
| 
 | ||||
|                 set.Add(s, true); | ||||
|             } | ||||
| 
 | ||||
|             set.Process(); | ||||
|             var errors = set.GetErrors(); | ||||
|             CSharpCodeGenerator.ClearTypeNames(); | ||||
|             var files = CSharpCodeGenerator.Default.Generate(set); | ||||
| 
 | ||||
|             foreach (var file in files) | ||||
|             { | ||||
|                 CSharpCodeGenerator.ClearTypeNames(); | ||||
|                 var full_file_name = file.Name; | ||||
|                 int index = full_file_name.LastIndexOf('.'); | ||||
|                 var file_name = index > 0 ? full_file_name.Substring(0, index) : full_file_name; | ||||
|                 file_name = file_name.ToLower(); | ||||
|                 var dest_filename = $"{NameNormalizer.AutoCapitalize(file_name)}.cs"; | ||||
|                 //var path = Path.Combine(outpath, file.Name); | ||||
|                 var path = Path.Combine(outpath, dest_filename); | ||||
|                 File.WriteAllText(path, file.Text); | ||||
| 
 | ||||
|                 Debug.Log($"Generated cs file for {full_file_name.Replace(".cs", ".proto")} successfully to: {path}"); | ||||
|             } | ||||
| 
 | ||||
|             EditorUtility.DisplayDialog("Complete", | ||||
|                 "Proto文件已转CS,详细请看控制台输出" + | ||||
|                 "\n" + | ||||
|                 "Proto files has been convert into CS files, please go to console and view details", | ||||
|                 "Close window"); | ||||
| 
 | ||||
|             if (win != null) | ||||
|             { | ||||
|                 win.Close(); | ||||
|             } | ||||
| 
 | ||||
|             AssetDatabase.Refresh(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0995aad32e8324ae48ae9e676c5bf38c | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 6def9201877ce4d4797b547c57cdeee0 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,787 @@ | |||
| using Google.Protobuf.Reflection; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     /// <summary> | ||||
|     /// A coded generator that writes C# | ||||
|     /// </summary> | ||||
|     public class CSharpCodeGenerator : CommonCodeGenerator | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Reusable code-generator instance | ||||
|         /// </summary> | ||||
|         public static CSharpCodeGenerator Default { get; } = new CSharpCodeGenerator(); | ||||
|         /// <summary> | ||||
|         /// Create a new CSharpCodeGenerator instance | ||||
|         /// </summary> | ||||
|         protected CSharpCodeGenerator() { } | ||||
|         /// <summary> | ||||
|         /// Returns the language name | ||||
|         /// </summary> | ||||
|         public override string Name => "C#"; | ||||
|         /// <summary> | ||||
|         /// Returns the default file extension | ||||
|         /// </summary> | ||||
|         protected override string DefaultFileExtension => "cs"; | ||||
|         /// <summary> | ||||
|         /// Escapes language keywords | ||||
|         /// </summary> | ||||
|         protected override string Escape(string identifier) | ||||
|         { | ||||
|             switch (identifier) | ||||
|             { | ||||
|                 case "abstract": | ||||
|                 case "event": | ||||
|                 case "new": | ||||
|                 case "struct": | ||||
|                 case "as": | ||||
|                 case "explicit": | ||||
|                 case "null": | ||||
|                 case "switch": | ||||
|                 case "base": | ||||
|                 case "extern": | ||||
|                 case "object": | ||||
|                 case "this": | ||||
|                 case "bool": | ||||
|                 case "false": | ||||
|                 case "operator": | ||||
|                 case "throw": | ||||
|                 case "break": | ||||
|                 case "finally": | ||||
|                 case "out": | ||||
|                 case "true": | ||||
|                 case "byte": | ||||
|                 case "fixed": | ||||
|                 case "override": | ||||
|                 case "try": | ||||
|                 case "case": | ||||
|                 case "float": | ||||
|                 case "params": | ||||
|                 case "typeof": | ||||
|                 case "catch": | ||||
|                 case "for": | ||||
|                 case "private": | ||||
|                 case "uint": | ||||
|                 case "char": | ||||
|                 case "foreach": | ||||
|                 case "protected": | ||||
|                 case "ulong": | ||||
|                 case "checked": | ||||
|                 case "goto": | ||||
|                 case "public": | ||||
|                 case "unchecked": | ||||
|                 case "class": | ||||
|                 case "if": | ||||
|                 case "readonly": | ||||
|                 case "unsafe": | ||||
|                 case "const": | ||||
|                 case "implicit": | ||||
|                 case "ref": | ||||
|                 case "ushort": | ||||
|                 case "continue": | ||||
|                 case "in": | ||||
|                 case "return": | ||||
|                 case "using": | ||||
|                 case "decimal": | ||||
|                 case "int": | ||||
|                 case "sbyte": | ||||
|                 case "virtual": | ||||
|                 case "default": | ||||
|                 case "interface": | ||||
|                 case "sealed": | ||||
|                 case "volatile": | ||||
|                 case "delegate": | ||||
|                 case "internal": | ||||
|                 case "short": | ||||
|                 case "void": | ||||
|                 case "do": | ||||
|                 case "is": | ||||
|                 case "sizeof": | ||||
|                 case "while": | ||||
|                 case "double": | ||||
|                 case "lock": | ||||
|                 case "stackalloc": | ||||
|                 case "else": | ||||
|                 case "long": | ||||
|                 case "static": | ||||
|                 case "enum": | ||||
|                 case "namespace": | ||||
|                 case "string": | ||||
|                     return "@" + identifier; | ||||
|                 default: | ||||
|                     return identifier; | ||||
|             } | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Start a file | ||||
|         /// </summary> | ||||
|         protected override void WriteFileHeader(GeneratorContext ctx, FileDescriptorProto file, ref object state) | ||||
|         { | ||||
|             ctx.WriteLine("// This file was generated by a tool; you should avoid making direct changes.") | ||||
|                .WriteLine("// Consider using 'partial classes' to extend these types") | ||||
|                .WriteLine($"// Input: {Path.GetFileName(ctx.File.Name)}").WriteLine() | ||||
|                .WriteLine("#pragma warning disable CS1591, CS0612, CS3021").WriteLine(); | ||||
| 
 | ||||
| 
 | ||||
|             var @namespace = ctx.NameNormalizer.GetName(file); | ||||
| 
 | ||||
|             if (!string.IsNullOrWhiteSpace(@namespace)) | ||||
|             { | ||||
|                 state = @namespace; | ||||
|                 ctx.WriteLine($"namespace {@namespace}"); | ||||
|                 ctx.WriteLine("{").Indent().WriteLine(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// End a file | ||||
|         /// </summary> | ||||
|         protected override void WriteFileFooter(GeneratorContext ctx, FileDescriptorProto file, ref object state) | ||||
|         { | ||||
|             var @namespace = (string)state; | ||||
|             if (!string.IsNullOrWhiteSpace(@namespace)) | ||||
|             { | ||||
|                 ctx.Outdent().WriteLine("}").WriteLine(); | ||||
|             } | ||||
| 
 | ||||
|             ctx.WriteLine("#pragma warning restore CS1591, CS0612, CS3021"); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Start an enum | ||||
|         /// </summary> | ||||
|         protected override void WriteEnumHeader(GeneratorContext ctx, EnumDescriptorProto obj, ref object state) | ||||
|         { | ||||
|             var name = ctx.NameNormalizer.GetName(obj); | ||||
|             var tw = ctx.Write($@"[global::ProtoBuf.ProtoContract("); | ||||
|             if (name != obj.Name) tw.Write($@"Name = @""{obj.Name}"""); | ||||
|             tw.WriteLine(")]"); | ||||
|             WriteOptions(ctx, obj.Options); | ||||
|             ctx.WriteLine($"{GetAccess(GetAccess(obj))} enum {Escape(name)}").WriteLine("{").Indent(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// End an enum | ||||
|         /// </summary> | ||||
| 
 | ||||
|         protected override void WriteEnumFooter(GeneratorContext ctx, EnumDescriptorProto obj, ref object state) | ||||
|         { | ||||
|             ctx.Outdent().WriteLine("}").WriteLine(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Write an enum value | ||||
|         /// </summary> | ||||
|         protected override void WriteEnumValue(GeneratorContext ctx, EnumValueDescriptorProto obj, ref object state) | ||||
|         { | ||||
|             var name = ctx.NameNormalizer.GetName(obj); | ||||
|             if (name != obj.Name) | ||||
|             { | ||||
|                 var tw = ctx.Write($@"[global::ProtoBuf.ProtoEnum("); | ||||
|                 tw.Write($@"Name = @""{obj.Name}"""); | ||||
|                 tw.WriteLine(")]"); | ||||
|             } | ||||
|              | ||||
|             WriteOptions(ctx, obj.Options); | ||||
|             ctx.WriteLine($"{Escape(name)} = {obj.Number},"); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// End a message | ||||
|         /// </summary> | ||||
|         protected override void WriteMessageFooter(GeneratorContext ctx, DescriptorProto obj, ref object state) | ||||
|         { | ||||
|             ctx.Outdent().WriteLine("}").WriteLine(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Start a message | ||||
|         /// </summary> | ||||
|         protected override void WriteMessageHeader(GeneratorContext ctx, DescriptorProto obj, ref object state) | ||||
|         { | ||||
|             var name = ctx.NameNormalizer.GetName(obj); | ||||
|             GetTypeName2(obj.FullyQualifiedName); | ||||
|             var tw = ctx.Write($@"[global::ProtoBuf.ProtoContract("); | ||||
|             if (name != obj.Name) tw.Write($@"Name = @""{obj.Name}"""); | ||||
|             tw.WriteLine(")]"); | ||||
|             WriteOptions(ctx, obj.Options); | ||||
|             tw = ctx.Write($"{GetAccess(GetAccess(obj))} partial class {Escape(name)}"); | ||||
|             if (obj.ExtensionRanges.Count != 0) tw.Write(" : global::ProtoBuf.IExtensible"); | ||||
|             tw.WriteLine(); | ||||
|             ctx.WriteLine("{").Indent(); | ||||
|             if (obj.Options?.MessageSetWireFormat == true) | ||||
|             { | ||||
|                 ctx.WriteLine("#error message_set_wire_format is not currently implemented").WriteLine(); | ||||
|             } | ||||
|             if (obj.ExtensionRanges.Count != 0) | ||||
|             { | ||||
|                 ctx.WriteLine($"private global::ProtoBuf.IExtension {FieldPrefix}extensionData;") | ||||
|                     .WriteLine($"global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)").Indent() | ||||
|                     .WriteLine($"=> global::ProtoBuf.Extensible.GetExtensionObject(ref {FieldPrefix}extensionData, createIfMissing);").Outdent().WriteLine(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static void WriteOptions<T>(GeneratorContext ctx, T obj) where T : class, ISchemaOptions | ||||
|         { | ||||
|             if (obj == null) return; | ||||
|             if (obj.Deprecated) | ||||
|             { | ||||
|                 ctx.WriteLine($"[global::System.Obsolete]"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const string FieldPrefix = "__pbn__"; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Get the language specific keyword representing an access level | ||||
|         /// </summary> | ||||
|         public override string GetAccess(Access access) | ||||
|         { | ||||
|             switch (access) | ||||
|             { | ||||
|                 case Access.Internal: return "internal"; | ||||
|                 case Access.Public: return "public"; | ||||
|                 case Access.Private: return "private"; | ||||
|                 default: return base.GetAccess(access); | ||||
|             } | ||||
|         } | ||||
|         static HashSet<string> TypeNames2 = new HashSet<string>(); | ||||
|         static string GetTypeName2(string type) { | ||||
|             if (type.StartsWith(".")) { type = type.Substring(1); } | ||||
|             TypeNames2.Add(type); | ||||
|             return type; | ||||
|         } | ||||
| 		public static void ClearTypeNames(){ | ||||
| 			TypeNames2.Clear (); | ||||
| 		} | ||||
|         /// <summary> | ||||
|         /// Write a field | ||||
|         /// </summary> | ||||
|         protected override void WriteField(GeneratorContext ctx, FieldDescriptorProto obj, ref object state, OneOfStub[] oneOfs) | ||||
|         { | ||||
|             string dataFormat; | ||||
|             var typeName = GetTypeName(ctx, obj, out dataFormat, out var isMap); | ||||
|             if (isMap) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             var name = ctx.NameNormalizer.GetName(obj); | ||||
|             var tw = ctx.Write($@"[global::ProtoBuf.ProtoMember({obj.Number}"); | ||||
| 
 | ||||
|             if (!string.IsNullOrWhiteSpace(dataFormat)) | ||||
|             { | ||||
|                 tw.Write($", (int)global::ProtoBuf.DataFormat.{dataFormat}"); | ||||
|             } | ||||
|             if (name != obj.Name) | ||||
|             { | ||||
|                 tw.Write($@", Name = @""{obj.Name}"""); | ||||
|             } | ||||
|             var options = obj.Options?.GetOptions(); | ||||
|             if (options?.AsReference == true) | ||||
|             { | ||||
|                 tw.Write($@", AsReference = true"); | ||||
|             } | ||||
|             if (options?.DynamicType == true) | ||||
|             { | ||||
|                 tw.Write($@", DynamicType = true"); | ||||
|             } | ||||
| 
 | ||||
|             bool isOptional = obj.label == FieldDescriptorProto.Label.LabelOptional; | ||||
|             bool isRepeated = obj.label == FieldDescriptorProto.Label.LabelRepeated; | ||||
|             // Only needed by ILRuntime | ||||
|             /*if (isRepeated && obj.type == FieldDescriptorProto.Type.TypeMessage) | ||||
|             { | ||||
|                 tw.Write($@", TypeName = ""{GetTypeName2(obj.TypeName)}"""); | ||||
|             }*/ | ||||
|             OneOfStub oneOf = obj.ShouldSerializeOneofIndex() ? oneOfs?[obj.OneofIndex] : null; | ||||
|             if (oneOf != null && oneOf.CountTotal == 1) | ||||
|             { | ||||
|                 oneOf = null; // not really a one-of, then! | ||||
|             } | ||||
|             bool explicitValues = isOptional && oneOf == null && ctx.Syntax == FileDescriptorProto.SyntaxProto2 | ||||
|                 && obj.type != FieldDescriptorProto.Type.TypeMessage | ||||
|                 && obj.type != FieldDescriptorProto.Type.TypeGroup; | ||||
| 
 | ||||
| 
 | ||||
|             string defaultValue = null; | ||||
|             bool suppressDefaultAttribute = !isOptional; | ||||
|             if (isOptional || obj.type == FieldDescriptorProto.Type.TypeEnum) | ||||
|             { | ||||
| 				//GetTypeName2(obj.TypeName); | ||||
|                 defaultValue = obj.DefaultValue; | ||||
| 
 | ||||
|                 if (obj.type == FieldDescriptorProto.Type.TypeString) | ||||
|                 { | ||||
|                     defaultValue = string.IsNullOrEmpty(defaultValue) ? "\"\"" | ||||
|                         : ("@\"" + (defaultValue ?? "").Replace("\"", "\"\"") + "\""); | ||||
|                 } | ||||
|                 else if (obj.type == FieldDescriptorProto.Type.TypeDouble) | ||||
|                 { | ||||
|                     switch (defaultValue) | ||||
|                     { | ||||
|                         case "inf": defaultValue = "double.PositiveInfinity"; break; | ||||
|                         case "-inf": defaultValue = "double.NegativeInfinity"; break; | ||||
|                         case "nan": defaultValue = "double.NaN"; break; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (obj.type == FieldDescriptorProto.Type.TypeFloat) | ||||
|                 { | ||||
|                     switch (defaultValue) | ||||
|                     { | ||||
|                         case "inf": defaultValue = "float.PositiveInfinity"; break; | ||||
|                         case "-inf": defaultValue = "float.NegativeInfinity"; break; | ||||
|                         case "nan": defaultValue = "float.NaN"; break; | ||||
|                     } | ||||
|                 } | ||||
|                 else if (obj.type == FieldDescriptorProto.Type.TypeEnum) | ||||
|                 { | ||||
|                     var enumType = ctx.TryFind<EnumDescriptorProto>(obj.TypeName); | ||||
|                     if (enumType != null) | ||||
|                     { | ||||
|                         EnumValueDescriptorProto found = null; | ||||
|                         if (!string.IsNullOrEmpty(defaultValue)) | ||||
|                         { | ||||
|                             found = enumType.Values.FirstOrDefault(x => x.Name == defaultValue); | ||||
|                         } | ||||
|                         else if (ctx.Syntax == FileDescriptorProto.SyntaxProto2) | ||||
|                         { | ||||
|                             // find the first one; if that is a zero, we don't need it after all | ||||
|                             found = enumType.Values.FirstOrDefault(); | ||||
|                             if(found != null && found.Number == 0) | ||||
|                             { | ||||
|                                 if(!isOptional) found = null; // we don't need it after all | ||||
|                             } | ||||
|                         } | ||||
|                         // for proto3 the default is 0, so no need to do anything - GetValueOrDefault() will do it all | ||||
| 
 | ||||
|                         if (found != null) | ||||
|                         { | ||||
|                             defaultValue = ctx.NameNormalizer.GetName(found); | ||||
|                         } | ||||
|                         if (!string.IsNullOrWhiteSpace(defaultValue)) | ||||
|                         { | ||||
|                             //defaultValue = ctx.NameNormalizer.GetName(enumType) + "." + defaultValue; | ||||
| 
 | ||||
| 							defaultValue = "global::"+enumType.FullyQualifiedName.Substring(1) + "." + defaultValue; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             if (obj.IsPacked(ctx.Syntax)) | ||||
|             { | ||||
|                 tw.Write($", IsPacked = true"); | ||||
|             } | ||||
|             if (obj.label == FieldDescriptorProto.Label.LabelRequired) | ||||
|             { | ||||
|                 tw.Write($", IsRequired = true"); | ||||
|             } | ||||
|             tw.WriteLine(")]"); | ||||
|             if (!isRepeated && !string.IsNullOrWhiteSpace(defaultValue) && !suppressDefaultAttribute) | ||||
|             { | ||||
|                 ctx.WriteLine($"[global::System.ComponentModel.DefaultValue({defaultValue})]"); | ||||
|             } | ||||
|             WriteOptions(ctx, obj.Options); | ||||
|             if (isRepeated) | ||||
|             { | ||||
|                 var mapMsgType = isMap ? ctx.TryFind<DescriptorProto>(obj.TypeName) : null; | ||||
|                 if (mapMsgType != null) | ||||
|                 { | ||||
|                     string keyDataFormat; | ||||
|                     bool _; | ||||
|                     var keyTypeName = GetTypeName(ctx, mapMsgType.Fields.Single(x => x.Number == 1), | ||||
|                         out keyDataFormat, out _); | ||||
|                     string valueDataFormat; | ||||
|                     var valueTypeName = GetTypeName(ctx, mapMsgType.Fields.Single(x => x.Number == 2), | ||||
|                         out valueDataFormat, out _); | ||||
| 
 | ||||
|                     bool first = true; | ||||
|                     tw = ctx.Write($"[global::ProtoBuf.ProtoMap"); | ||||
|                     if (!string.IsNullOrWhiteSpace(keyDataFormat)) | ||||
|                     { | ||||
|                         tw.Write($"{(first ? "(" : ", ")}KeyFormat = global::ProtoBuf.DataFormat.{keyDataFormat}"); | ||||
|                         first = false; | ||||
|                     } | ||||
|                     if (!string.IsNullOrWhiteSpace(valueDataFormat)) | ||||
|                     { | ||||
|                         tw.Write($"{(first ? "(" : ", ")}ValueFormat = global::ProtoBuf.DataFormat.{valueDataFormat}"); | ||||
|                         first = false; | ||||
|                     } | ||||
|                     tw.WriteLine(first ? "]" : ")]"); | ||||
|                     ctx.WriteLine($"{GetAccess(GetAccess(obj))} global::System.Collections.Generic.Dictionary<{keyTypeName}, {valueTypeName}> {Escape(name)} {{ get; }} = new global::System.Collections.Generic.Dictionary<{keyTypeName}, {valueTypeName}>();"); | ||||
|                 } | ||||
|                 else if (UseArray(obj)) | ||||
|                 { | ||||
|                     ctx.WriteLine($"{GetAccess(GetAccess(obj))} {typeName}[] {Escape(name)} {{ get; set; }}"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ctx.WriteLine($"{GetAccess(GetAccess(obj))} global::System.Collections.Generic.List<{typeName}> {Escape(name)} {{ get; }} = new global::System.Collections.Generic.List<{typeName}>();"); | ||||
|                 } | ||||
|             } | ||||
|             else if (oneOf != null) | ||||
|             { | ||||
|                 var defValue = string.IsNullOrWhiteSpace(defaultValue) ? $"default({typeName})" : defaultValue; | ||||
|                 var fieldName = FieldPrefix + oneOf.OneOf.Name; | ||||
|                 var storage = oneOf.GetStorage(obj.type, obj.TypeName); | ||||
|                 ctx.WriteLine($"{GetAccess(GetAccess(obj))} {typeName} {Escape(name)}").WriteLine("{").Indent(); | ||||
| 
 | ||||
|                 switch (obj.type) | ||||
|                 { | ||||
|                     case FieldDescriptorProto.Type.TypeMessage: | ||||
|                     case FieldDescriptorProto.Type.TypeGroup: | ||||
|                     case FieldDescriptorProto.Type.TypeEnum: | ||||
|                     case FieldDescriptorProto.Type.TypeBytes: | ||||
|                     case FieldDescriptorProto.Type.TypeString: | ||||
|                         ctx.WriteLine($"get {{ return {fieldName}.Is({obj.Number}) ? (({typeName}){fieldName}.{storage}) : {defValue}; }}"); | ||||
|                         break; | ||||
|                     default: | ||||
|                         ctx.WriteLine($"get {{ return {fieldName}.Is({obj.Number}) ? {fieldName}.{storage} : {defValue}; }}"); | ||||
|                         break; | ||||
|                 } | ||||
|                 var unionType = oneOf.GetUnionType(); | ||||
|                 ctx.WriteLine($"set {{ {fieldName} = new global::ProtoBuf.{unionType}({obj.Number}, value); }}") | ||||
|                     .Outdent().WriteLine("}") | ||||
|                     .WriteLine($"{GetAccess(GetAccess(obj))} bool ShouldSerialize{name}() => {fieldName}.Is({obj.Number});") | ||||
|                     .WriteLine($"{GetAccess(GetAccess(obj))} void Reset{name}() => global::ProtoBuf.{unionType}.Reset(ref {fieldName}, {obj.Number});"); | ||||
| 
 | ||||
|                 if (oneOf.IsFirst()) | ||||
|                 { | ||||
|                     ctx.WriteLine().WriteLine($"private global::ProtoBuf.{unionType} {fieldName};"); | ||||
|                 } | ||||
|             } | ||||
|             else if (explicitValues) | ||||
|             { | ||||
|                 string fieldName = FieldPrefix + name, fieldType; | ||||
|                 bool isRef = false; | ||||
|                 switch (obj.type) | ||||
|                 { | ||||
|                     case FieldDescriptorProto.Type.TypeString: | ||||
|                     case FieldDescriptorProto.Type.TypeBytes: | ||||
|                         fieldType = typeName; | ||||
|                         isRef = true; | ||||
|                         break; | ||||
|                     default: | ||||
|                         fieldType = typeName + "?"; | ||||
|                         break; | ||||
|                 } | ||||
|                 ctx.WriteLine($"{GetAccess(GetAccess(obj))} {typeName} {Escape(name)}").WriteLine("{").Indent(); | ||||
|                 tw = ctx.Write($"get {{ return {fieldName}"); | ||||
|                 if (!string.IsNullOrWhiteSpace(defaultValue)) | ||||
|                 { | ||||
|                     tw.Write(" ?? "); | ||||
|                     tw.Write(defaultValue); | ||||
|                 } | ||||
|                 else if (!isRef) | ||||
|                 { | ||||
|                     tw.Write(".GetValueOrDefault()"); | ||||
|                 } | ||||
|                 tw.WriteLine("; }"); | ||||
|                 ctx.WriteLine($"set {{ {fieldName} = value; }}") | ||||
|                     .Outdent().WriteLine("}") | ||||
|                     .WriteLine($"{GetAccess(GetAccess(obj))} bool ShouldSerialize{name}() => {fieldName} != null;") | ||||
|                     .WriteLine($"{GetAccess(GetAccess(obj))} void Reset{name}() => {fieldName} = null;") | ||||
|                     .WriteLine($"private {fieldType} {fieldName};"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 tw = ctx.Write($"{GetAccess(GetAccess(obj))} {typeName} {Escape(name)} {{ get; set; }}"); | ||||
|                 if (!string.IsNullOrWhiteSpace(defaultValue)) tw.Write($" = {defaultValue};"); | ||||
|                 tw.WriteLine(); | ||||
|             } | ||||
|             ctx.WriteLine(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Starts an extgensions block | ||||
|         /// </summary> | ||||
|         protected override void WriteExtensionsHeader(GeneratorContext ctx, FileDescriptorProto obj, ref object state) | ||||
|         { | ||||
|             var name = obj?.Options?.GetOptions()?.ExtensionTypeName; | ||||
|             if (string.IsNullOrWhiteSpace(name)) name = "Extensions"; | ||||
|             ctx.WriteLine($"{GetAccess(GetAccess(obj))} static class {Escape(name)}").WriteLine("{").Indent(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Ends an extgensions block | ||||
|         /// </summary> | ||||
|         protected override void WriteExtensionsFooter(GeneratorContext ctx, FileDescriptorProto obj, ref object state) | ||||
|         { | ||||
|             ctx.Outdent().WriteLine("}"); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Starts an extensions block | ||||
|         /// </summary> | ||||
|         protected override void WriteExtensionsHeader(GeneratorContext ctx, DescriptorProto obj, ref object state) | ||||
|         { | ||||
|             var name = obj?.Options?.GetOptions()?.ExtensionTypeName; | ||||
|             if (string.IsNullOrWhiteSpace(name)) name = "Extensions"; | ||||
|             ctx.WriteLine($"{GetAccess(GetAccess(obj))} static class {Escape(name)}").WriteLine("{").Indent(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Ends an extensions block | ||||
|         /// </summary> | ||||
|         protected override void WriteExtensionsFooter(GeneratorContext ctx, DescriptorProto obj, ref object state) | ||||
|         { | ||||
|             ctx.Outdent().WriteLine("}"); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Write an extension | ||||
|         /// </summary> | ||||
|         protected override void WriteExtension(GeneratorContext ctx, FieldDescriptorProto field) | ||||
|         { | ||||
|             string dataFormat; | ||||
|             bool isMap; | ||||
|             var type = GetTypeName(ctx, field, out dataFormat, out isMap); | ||||
| 
 | ||||
|             if (isMap) | ||||
|             { | ||||
|                 ctx.WriteLine("#error map extensions not yet implemented"); | ||||
|             } | ||||
|             else if (field.label == FieldDescriptorProto.Label.LabelRepeated) | ||||
|             { | ||||
|                 ctx.WriteLine("#error repeated extensions not yet implemented"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var msg = ctx.TryFind<DescriptorProto>(field.Extendee); | ||||
|                 var extendee = MakeRelativeName(field, msg, ctx.NameNormalizer); | ||||
| 
 | ||||
|                 var @this = field.Parent is FileDescriptorProto ? "this " : ""; | ||||
|                 string name = ctx.NameNormalizer.GetName(field); | ||||
|                 var tw = ctx.WriteLine($"{GetAccess(GetAccess(field))} static {type} Get{name}({@this}{extendee} obj)") | ||||
|                     .Write($"=> obj == null ? default({type}) : global::ProtoBuf.Extensible.GetValue<{type}>(obj, {field.Number}"); | ||||
|                 if (!string.IsNullOrEmpty(dataFormat)) | ||||
|                 { | ||||
|                     tw.Write($", global::ProtoBuf.DataFormat.{dataFormat}"); | ||||
|                 } | ||||
|                 tw.WriteLine(");"); | ||||
|                 ctx.WriteLine(); | ||||
|                 //  GetValue<TValue>(IExtensible instance, int tag, DataFormat format) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static bool UseArray(FieldDescriptorProto field) | ||||
|         { | ||||
|             switch (field.type) | ||||
|             { | ||||
|                 case FieldDescriptorProto.Type.TypeBool: | ||||
|                 case FieldDescriptorProto.Type.TypeDouble: | ||||
|                 case FieldDescriptorProto.Type.TypeFixed32: | ||||
|                 case FieldDescriptorProto.Type.TypeFixed64: | ||||
|                 case FieldDescriptorProto.Type.TypeFloat: | ||||
|                 case FieldDescriptorProto.Type.TypeInt32: | ||||
|                 case FieldDescriptorProto.Type.TypeInt64: | ||||
|                 case FieldDescriptorProto.Type.TypeSfixed32: | ||||
|                 case FieldDescriptorProto.Type.TypeSfixed64: | ||||
|                 case FieldDescriptorProto.Type.TypeSint32: | ||||
|                 case FieldDescriptorProto.Type.TypeSint64: | ||||
|                 case FieldDescriptorProto.Type.TypeUint32: | ||||
|                 case FieldDescriptorProto.Type.TypeUint64: | ||||
|                     return true; | ||||
|                 default: | ||||
|                     return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private string GetTypeName(GeneratorContext ctx, FieldDescriptorProto field, out string dataFormat, out bool isMap) | ||||
|         { | ||||
|             dataFormat = ""; | ||||
|             isMap = false; | ||||
|             switch (field.type) | ||||
|             { | ||||
|                 case FieldDescriptorProto.Type.TypeDouble: | ||||
|                     return "double"; | ||||
|                 case FieldDescriptorProto.Type.TypeFloat: | ||||
|                     return "float"; | ||||
|                 case FieldDescriptorProto.Type.TypeBool: | ||||
|                     return "bool"; | ||||
|                 case FieldDescriptorProto.Type.TypeString: | ||||
|                     return "string"; | ||||
|                 case FieldDescriptorProto.Type.TypeSint32: | ||||
|                     dataFormat = nameof(DataFormat.ZigZag); | ||||
|                     return "int"; | ||||
|                 case FieldDescriptorProto.Type.TypeInt32: | ||||
|                     return "int"; | ||||
|                 case FieldDescriptorProto.Type.TypeSfixed32: | ||||
|                     dataFormat = nameof(DataFormat.FixedSize); | ||||
|                     return "int"; | ||||
|                 case FieldDescriptorProto.Type.TypeSint64: | ||||
|                     dataFormat = nameof(DataFormat.ZigZag); | ||||
|                     return "long"; | ||||
|                 case FieldDescriptorProto.Type.TypeInt64: | ||||
|                     return "long"; | ||||
|                 case FieldDescriptorProto.Type.TypeSfixed64: | ||||
|                     dataFormat = nameof(DataFormat.FixedSize); | ||||
|                     return "long"; | ||||
|                 case FieldDescriptorProto.Type.TypeFixed32: | ||||
|                     dataFormat = nameof(DataFormat.FixedSize); | ||||
|                     return "uint"; | ||||
|                 case FieldDescriptorProto.Type.TypeUint32: | ||||
|                     return "uint"; | ||||
|                 case FieldDescriptorProto.Type.TypeFixed64: | ||||
|                     dataFormat = nameof(DataFormat.FixedSize); | ||||
|                     return "ulong"; | ||||
|                 case FieldDescriptorProto.Type.TypeUint64: | ||||
|                     return "ulong"; | ||||
|                 case FieldDescriptorProto.Type.TypeBytes: | ||||
|                     return "byte[]"; | ||||
|                 case FieldDescriptorProto.Type.TypeEnum: | ||||
|                     switch (field.TypeName) | ||||
|                     { | ||||
|                         case ".bcl.DateTime.DateTimeKind": | ||||
|                             return "global::System.DateTimeKind"; | ||||
|                     } | ||||
|                     var enumType = ctx.TryFind<EnumDescriptorProto>(field.TypeName); | ||||
|                     return MakeRelativeName(field, enumType, ctx.NameNormalizer); | ||||
|                 case FieldDescriptorProto.Type.TypeGroup: | ||||
|                 case FieldDescriptorProto.Type.TypeMessage: | ||||
|                     switch (field.TypeName) | ||||
|                     { | ||||
|                         case WellKnownTypeTimestamp: | ||||
|                             dataFormat = "WellKnown"; | ||||
|                             return "global::System.DateTime?"; | ||||
|                         case WellKnownTypeDuration: | ||||
|                             dataFormat = "WellKnown"; | ||||
|                             return "global::System.TimeSpan?"; | ||||
|                         case ".bcl.NetObjectProxy": | ||||
|                             return "object"; | ||||
|                         case ".bcl.DateTime": | ||||
|                             return "global::System.DateTime?"; | ||||
|                         case ".bcl.TimeSpan": | ||||
|                             return "global::System.TimeSpan?"; | ||||
|                         case ".bcl.Decimal": | ||||
|                             return "decimal?"; | ||||
|                         case ".bcl.Guid": | ||||
|                             return "global::System.Guid?"; | ||||
|                     } | ||||
|                     var msgType = ctx.TryFind<DescriptorProto>(field.TypeName); | ||||
|                     if (field.type == FieldDescriptorProto.Type.TypeGroup) | ||||
|                     { | ||||
|                         dataFormat = nameof(DataFormat.Group); | ||||
|                     } | ||||
|                     isMap = msgType?.Options?.MapEntry ?? false; | ||||
|                     return MakeRelativeName(field, msgType, ctx.NameNormalizer); | ||||
|                 default: | ||||
|                     return field.TypeName; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private string MakeRelativeName(FieldDescriptorProto field, IType target, NameNormalizer normalizer) | ||||
|         { | ||||
|             if (target == null) return Escape(field.TypeName); // the only thing we know | ||||
| 
 | ||||
|             var declaringType = field.Parent; | ||||
| 
 | ||||
|             if (declaringType is IType) | ||||
|             { | ||||
|                 var name = FindNameFromCommonAncestor((IType)declaringType, target, normalizer); | ||||
|                 if (!string.IsNullOrWhiteSpace(name)) return name; | ||||
|             } | ||||
|             return Escape(field.TypeName); // give up! | ||||
|         } | ||||
| 
 | ||||
|         // k, what we do is; we have two types; each knows the parent, but nothing else, so: | ||||
|         // for each, use a stack to build the ancestry tree - the "top" of the stack will be the | ||||
|         // package, the bottom of the stack will be the type itself. They will often be stacks | ||||
|         // of different heights. | ||||
|         // | ||||
|         // Find how many is in the smallest stack; now take that many items, in turn, until we | ||||
|         // get something that is different (at which point, put that one back on the stack), or  | ||||
|         // we run out of items in one of the stacks. | ||||
|         // | ||||
|         // There are now two options: | ||||
|         // - we ran out of things in the "target" stack - in which case, they are common enough to not | ||||
|         //   need any resolution - just give back the fixed name | ||||
|         // - we have things left in the "target" stack - in which case we have found a common ancestor, | ||||
|         //   or the target is a descendent; either way, just concat what is left (including the package | ||||
|         //   if the package itself was different) | ||||
| 
 | ||||
|         private string FindNameFromCommonAncestor(IType declaring, IType target, NameNormalizer normalizer) | ||||
|         { | ||||
|             // trivial case; asking for self, or asking for immediate child | ||||
|             if (ReferenceEquals(declaring, target) || ReferenceEquals(declaring, target.Parent)) | ||||
|             { | ||||
|                 if (target is DescriptorProto) return Escape(normalizer.GetName((DescriptorProto)target)); | ||||
|                 if (target is EnumDescriptorProto) return Escape(normalizer.GetName((EnumDescriptorProto)target)); | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             var origTarget = target; | ||||
|             var xStack = new Stack<IType>(); | ||||
| 
 | ||||
|             while (declaring != null) | ||||
|             { | ||||
|                 xStack.Push(declaring); | ||||
|                 declaring = declaring.Parent; | ||||
|             } | ||||
|             var yStack = new Stack<IType>(); | ||||
| 
 | ||||
|             while (target != null) | ||||
|             { | ||||
|                 yStack.Push(target); | ||||
|                 target = target.Parent; | ||||
|             } | ||||
|             int lim = Math.Min(xStack.Count, yStack.Count); | ||||
|             for (int i = 0; i < lim; i++) | ||||
|             { | ||||
|                 declaring = xStack.Peek(); | ||||
|                 target = yStack.Pop(); | ||||
|                 if (!ReferenceEquals(target, declaring)) | ||||
|                 { | ||||
|                     // special-case: if both are the package (file), and they have the same namespace: we're OK | ||||
|                     if (target is FileDescriptorProto && declaring is FileDescriptorProto && | ||||
|                         normalizer.GetName((FileDescriptorProto)declaring) == normalizer.GetName((FileDescriptorProto)target)) | ||||
|                     { | ||||
|                         // that's fine, keep going | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         // put it back | ||||
|                         yStack.Push(target); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             // if we used everything, then the target is an ancestor-or-self | ||||
|             if (yStack.Count == 0) | ||||
|             { | ||||
|                 target = origTarget; | ||||
|                 if (target is DescriptorProto) return Escape(normalizer.GetName((DescriptorProto)target)); | ||||
|                 if (target is EnumDescriptorProto) return Escape(normalizer.GetName((EnumDescriptorProto)target)); | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             var sb = new StringBuilder(); | ||||
|             while (yStack.Count != 0) | ||||
|             { | ||||
|                 target = yStack.Pop(); | ||||
| 
 | ||||
|                 string nextName; | ||||
|                 if (target is FileDescriptorProto) nextName = normalizer.GetName((FileDescriptorProto)target); | ||||
|                 else if (target is DescriptorProto) nextName = normalizer.GetName((DescriptorProto)target); | ||||
|                 else if (target is EnumDescriptorProto) nextName = normalizer.GetName((EnumDescriptorProto)target); | ||||
|                 else return null; | ||||
| 
 | ||||
|                 if (!string.IsNullOrWhiteSpace(nextName)) | ||||
|                 { | ||||
|                     if (sb.Length == 0 && target is FileDescriptorProto) sb.Append("global::"); | ||||
|                     else if (sb.Length != 0) sb.Append('.'); | ||||
|                     sb.Append(Escape(nextName)); | ||||
|                 } | ||||
|             } | ||||
|             return sb.ToString(); | ||||
|         } | ||||
| 
 | ||||
|         static bool IsAncestorOrSelf(IType parent, IType child) | ||||
|         { | ||||
|             while (parent != null) | ||||
|             { | ||||
|                 if (ReferenceEquals(parent, child)) return true; | ||||
|                 parent = parent.Parent; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         const string WellKnownTypeTimestamp = ".google.protobuf.Timestamp", | ||||
|                      WellKnownTypeDuration = ".google.protobuf.Duration"; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 8f18de68ea6964dc78de6459348acefe | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,158 @@ | |||
| using Google.Protobuf.Reflection; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     partial class CommonCodeGenerator | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Represents the union summary of a one-of declaration | ||||
|         /// </summary> | ||||
|         protected class OneOfStub | ||||
|         { | ||||
|             /// <summary> | ||||
|             /// The underlying descriptor | ||||
|             /// </summary> | ||||
|             public OneofDescriptorProto OneOf { get; } | ||||
| 
 | ||||
|             internal OneOfStub(OneofDescriptorProto decl) | ||||
|             { | ||||
|                 OneOf = decl; | ||||
|             } | ||||
|             internal int Count32 { get; private set; } | ||||
|             internal int Count64 { get; private set; } | ||||
|             internal int Count128 { get; private set; } | ||||
|             internal int CountRef { get; private set; } | ||||
|             internal int CountTotal => CountRef + Count32 + Count64; | ||||
| 
 | ||||
|             private void AccountFor(FieldDescriptorProto.Type type, string typeName) | ||||
|             { | ||||
|                 switch (type) | ||||
|                 { | ||||
|                     case FieldDescriptorProto.Type.TypeBool: | ||||
|                     case FieldDescriptorProto.Type.TypeEnum: | ||||
|                     case FieldDescriptorProto.Type.TypeFixed32: | ||||
|                     case FieldDescriptorProto.Type.TypeFloat: | ||||
|                     case FieldDescriptorProto.Type.TypeInt32: | ||||
|                     case FieldDescriptorProto.Type.TypeSfixed32: | ||||
|                     case FieldDescriptorProto.Type.TypeSint32: | ||||
|                     case FieldDescriptorProto.Type.TypeUint32: | ||||
|                         Count32++; | ||||
|                         break; | ||||
|                     case FieldDescriptorProto.Type.TypeDouble: | ||||
|                     case FieldDescriptorProto.Type.TypeFixed64: | ||||
|                     case FieldDescriptorProto.Type.TypeInt64: | ||||
|                     case FieldDescriptorProto.Type.TypeSfixed64: | ||||
|                     case FieldDescriptorProto.Type.TypeSint64: | ||||
|                     case FieldDescriptorProto.Type.TypeUint64: | ||||
|                         Count32++; | ||||
|                         Count64++; | ||||
|                         break; | ||||
|                     case FieldDescriptorProto.Type.TypeMessage: | ||||
|                         switch(typeName) | ||||
|                         { | ||||
|                             case ".google.protobuf.Timestamp": | ||||
|                             case ".google.protobuf.Duration": | ||||
|                                 Count64++; | ||||
|                                 break; | ||||
|                             case ".bcl.Guid": | ||||
|                                 Count128++; | ||||
|                                 break; | ||||
|                             default: | ||||
|                                 CountRef++; | ||||
|                                 break; | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         CountRef++; | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|             internal string GetStorage(FieldDescriptorProto.Type type, string typeName) | ||||
|             { | ||||
|                 switch (type) | ||||
|                 { | ||||
|                     case FieldDescriptorProto.Type.TypeBool: | ||||
|                         return "Boolean"; | ||||
|                     case FieldDescriptorProto.Type.TypeInt32: | ||||
|                     case FieldDescriptorProto.Type.TypeSfixed32: | ||||
|                     case FieldDescriptorProto.Type.TypeSint32: | ||||
|                     case FieldDescriptorProto.Type.TypeFixed32: | ||||
|                     case FieldDescriptorProto.Type.TypeEnum: | ||||
|                         return "Int32"; | ||||
|                     case FieldDescriptorProto.Type.TypeFloat: | ||||
|                         return "Single"; | ||||
|                     case FieldDescriptorProto.Type.TypeUint32: | ||||
|                         return "UInt32"; | ||||
|                     case FieldDescriptorProto.Type.TypeDouble: | ||||
|                         return "Double"; | ||||
|                     case FieldDescriptorProto.Type.TypeFixed64: | ||||
|                     case FieldDescriptorProto.Type.TypeInt64: | ||||
|                     case FieldDescriptorProto.Type.TypeSfixed64: | ||||
|                     case FieldDescriptorProto.Type.TypeSint64: | ||||
|                         return "Int64"; | ||||
|                     case FieldDescriptorProto.Type.TypeUint64: | ||||
|                         return "UInt64"; | ||||
|                     case FieldDescriptorProto.Type.TypeMessage: | ||||
|                         switch (typeName) | ||||
|                         { | ||||
|                             case ".google.protobuf.Timestamp": | ||||
|                                 return "DateTime"; | ||||
|                             case ".google.protobuf.Duration": | ||||
|                                 return "TimeSpan"; | ||||
|                             case ".bcl.Guid": | ||||
|                                 return "Guid"; | ||||
|                             default: | ||||
|                                 return "Object"; | ||||
|                         } | ||||
|                     default: | ||||
|                         return "Object"; | ||||
|                 } | ||||
|             } | ||||
|             internal static OneOfStub[] Build(GeneratorContext context, DescriptorProto message) | ||||
|             { | ||||
|                 if (message.OneofDecls.Count == 0) return null; | ||||
|                 var stubs = new OneOfStub[message.OneofDecls.Count]; | ||||
|                 int index = 0; | ||||
|                 foreach (var decl in message.OneofDecls) | ||||
|                 { | ||||
|                     stubs[index++] = new OneOfStub(decl); | ||||
|                 } | ||||
|                 foreach (var field in message.Fields) | ||||
|                 { | ||||
|                     if (field.ShouldSerializeOneofIndex()) | ||||
|                     { | ||||
|                         stubs[field.OneofIndex].AccountFor(field.type, field.TypeName); | ||||
|                     } | ||||
|                 } | ||||
|                 return stubs; | ||||
|             } | ||||
|             private bool isFirst = true; | ||||
|             internal bool IsFirst() | ||||
|             { | ||||
|                 if (isFirst) | ||||
|                 { | ||||
|                     isFirst = false; | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             internal string GetUnionType() | ||||
|             { | ||||
|                 if (Count128 != 0) | ||||
|                 { | ||||
|                     return CountRef == 0 ? "DiscriminatedUnion128" : "DiscriminatedUnion128Object"; | ||||
|                 } | ||||
|                 if (Count64 != 0) | ||||
|                 { | ||||
|                     return CountRef == 0 ? "DiscriminatedUnion64" : "DiscriminatedUnion64Object"; | ||||
|                 } | ||||
|                 if (Count32 != 0) | ||||
|                 { | ||||
|                     return CountRef == 0 ? "DiscriminatedUnion32" : "DiscriminatedUnion32Object"; | ||||
|                 } | ||||
|                 return "DiscriminatedUnionObject"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: ff61418fb53f3488c9fe4974974b436e | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,476 @@ | |||
| using Google.Protobuf.Reflection; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Abstract root for a general purpose code-generator | ||||
|     /// </summary> | ||||
|     public abstract class CodeGenerator | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// The logical name of this code generator | ||||
|         /// </summary> | ||||
|         public abstract string Name { get; } | ||||
|         /// <summary> | ||||
|         /// Get a string representation of the instance | ||||
|         /// </summary> | ||||
|         public override string ToString() => Name; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Execute the code generator against a FileDescriptorSet, yielding a sequence of files | ||||
|         /// </summary> | ||||
|         public abstract IEnumerable<CodeFile> Generate(FileDescriptorSet set, NameNormalizer normalizer = null); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Eexecute this code generator against a code file | ||||
|         /// </summary> | ||||
|         public CompilerResult Compile(CodeFile file) => Compile(new[] { file }); | ||||
|         /// <summary> | ||||
|         /// Eexecute this code generator against a set of code file | ||||
|         /// </summary> | ||||
|         public CompilerResult Compile(params CodeFile[] files) | ||||
|         { | ||||
|             var set = new FileDescriptorSet(); | ||||
|             foreach (var file in files) | ||||
|             { | ||||
|                 using (var reader = new StringReader(file.Text)) | ||||
|                 { | ||||
|                     Console.WriteLine($"Parsing {file.Name}..."); | ||||
|                     set.Add(file.Name, true, reader); | ||||
|                 } | ||||
|             } | ||||
|             set.Process(); | ||||
|             var results = new List<CodeFile>(); | ||||
|             var newErrors = new List<Error>(); | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 results.AddRange(Generate(set)); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 set.Errors.Add(new Error(default(Token), ex.Message, true)); | ||||
|             } | ||||
|             var errors = set.GetErrors(); | ||||
| 
 | ||||
|             return new CompilerResult(errors, results.ToArray()); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// Abstract base class for a code generator that uses a visitor pattern | ||||
|     /// </summary> | ||||
|     public abstract partial class CommonCodeGenerator : CodeGenerator | ||||
|     { | ||||
|         private Access? GetAccess(IType parent) | ||||
|         { | ||||
|             if (parent is DescriptorProto) | ||||
|                 return GetAccess((DescriptorProto)parent); | ||||
|             if (parent is EnumDescriptorProto) | ||||
|                 return GetAccess((EnumDescriptorProto)parent); | ||||
|             if (parent is FileDescriptorProto) | ||||
|                 return GetAccess((FileDescriptorProto)parent); | ||||
|             return null; | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Obtain the access of an item, accounting for the model's hierarchy | ||||
|         /// </summary> | ||||
|         protected Access GetAccess(FileDescriptorProto obj) | ||||
|             => obj?.Options?.GetOptions()?.Access ?? Access.Public; | ||||
| 
 | ||||
|         static Access? NullIfInherit(Access? access) | ||||
|             => access == Access.Inherit ? null : access; | ||||
|         /// <summary> | ||||
|         /// Obtain the access of an item, accounting for the model's hierarchy | ||||
|         /// </summary> | ||||
|         protected Access GetAccess(DescriptorProto obj) | ||||
|             => NullIfInherit(obj?.Options?.GetOptions()?.Access) | ||||
|             ?? GetAccess(obj?.Parent) ?? Access.Public; | ||||
|         /// <summary> | ||||
|         /// Obtain the access of an item, accounting for the model's hierarchy | ||||
|         /// </summary> | ||||
|         protected Access GetAccess(FieldDescriptorProto obj) | ||||
|             => NullIfInherit(obj?.Options?.GetOptions()?.Access) | ||||
|             ?? GetAccess(obj?.Parent as IType) ?? Access.Public; | ||||
|         /// <summary> | ||||
|         /// Obtain the access of an item, accounting for the model's hierarchy | ||||
|         /// </summary> | ||||
|         protected Access GetAccess(EnumDescriptorProto obj) | ||||
|             => NullIfInherit(obj?.Options?.GetOptions()?.Access) | ||||
|                 ?? GetAccess(obj?.Parent) ?? Access.Public; | ||||
|         /// <summary> | ||||
|         /// Get the textual name of a given access level | ||||
|         /// </summary> | ||||
|         public virtual string GetAccess(Access access) | ||||
|             => access.ToString(); | ||||
|          | ||||
|         /// <summary> | ||||
|         /// The indentation used by this code generator | ||||
|         /// </summary> | ||||
|         public virtual string Indent => "    "; | ||||
|         /// <summary> | ||||
|         /// The file extension of the files generatred by this generator | ||||
|         /// </summary> | ||||
|         protected abstract string DefaultFileExtension { get; } | ||||
|         /// <summary> | ||||
|         /// Handle keyword escaping in the language of this code generator | ||||
|         /// </summary> | ||||
|         /// <param name="identifier"></param> | ||||
|         /// <returns></returns> | ||||
|         protected abstract string Escape(string identifier); | ||||
|         /// <summary> | ||||
|         /// Execute the code generator against a FileDescriptorSet, yielding a sequence of files | ||||
|         /// </summary> | ||||
|         public override IEnumerable<CodeFile> Generate(FileDescriptorSet set, NameNormalizer normalizer = null) | ||||
|         { | ||||
|             foreach (var file in set.Files) | ||||
|             { | ||||
|                 if (!file.IncludeInOutput) continue; | ||||
| 
 | ||||
|                 var fileName = Path.ChangeExtension(file.Name, DefaultFileExtension); | ||||
| 
 | ||||
|                 string generated; | ||||
|                 using (var buffer = new StringWriter()) | ||||
|                 { | ||||
|                     var ctx = new GeneratorContext(file, normalizer ?? NameNormalizer.Default, buffer, Indent); | ||||
| 
 | ||||
|                     ctx.BuildTypeIndex(); // populates for TryFind<T> | ||||
|                     WriteFile(ctx, file); | ||||
|                     generated = buffer.ToString(); | ||||
|                 } | ||||
|                 yield return new CodeFile(fileName, generated); | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Emits the code for a file in a descriptor-set | ||||
|         /// </summary> | ||||
|         protected virtual void WriteFile(GeneratorContext ctx, FileDescriptorProto obj) | ||||
|         { | ||||
|             var file = ctx.File; | ||||
|             object state = null; | ||||
|             WriteFileHeader(ctx, obj, ref state); | ||||
| 
 | ||||
|             foreach (var inner in file.MessageTypes) | ||||
|             { | ||||
|                 WriteMessage(ctx, inner); | ||||
|             } | ||||
|             foreach (var inner in file.EnumTypes) | ||||
|             { | ||||
|                 WriteEnum(ctx, inner); | ||||
|             } | ||||
|             foreach (var inner in file.Services) | ||||
|             { | ||||
|                 WriteService(ctx, inner); | ||||
|             } | ||||
|             if(file.Extensions.Count != 0) | ||||
|             { | ||||
|                 object extState = null; | ||||
|                 WriteExtensionsHeader(ctx, file, ref extState); | ||||
|                 foreach(var ext in file.Extensions) | ||||
|                 { | ||||
|                     WriteExtension(ctx, ext); | ||||
|                 } | ||||
|                 WriteExtensionsFooter(ctx, file, ref extState); | ||||
|             } | ||||
|             WriteFileFooter(ctx, obj, ref state); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Emit code representing an extension field | ||||
|         /// </summary> | ||||
|         protected virtual void WriteExtension(GeneratorContext ctx, FieldDescriptorProto ext) { } | ||||
|         /// <summary> | ||||
|         /// Emit code preceeding a set of extension fields | ||||
|         /// </summary> | ||||
|         protected virtual void WriteExtensionsHeader(GeneratorContext ctx, FileDescriptorProto file, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code following a set of extension fields | ||||
|         /// </summary> | ||||
|         protected virtual void WriteExtensionsFooter(GeneratorContext ctx, FileDescriptorProto file, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code preceeding a set of extension fields | ||||
|         /// </summary> | ||||
|         protected virtual void WriteExtensionsHeader(GeneratorContext ctx, DescriptorProto file, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code following a set of extension fields | ||||
|         /// </summary> | ||||
|         protected virtual void WriteExtensionsFooter(GeneratorContext ctx, DescriptorProto file, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code representing a service | ||||
|         /// </summary> | ||||
|         protected virtual void WriteService(GeneratorContext ctx, ServiceDescriptorProto obj) | ||||
|         { | ||||
|             object state = null; | ||||
|             WriteServiceHeader(ctx, obj, ref state); | ||||
|             foreach (var inner in obj.Methods) | ||||
|             { | ||||
|                 WriteServiceMethod(ctx, inner, ref state); | ||||
|             } | ||||
|             WriteServiceFooter(ctx, obj, ref state); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Emit code following a set of service methods | ||||
|         /// </summary> | ||||
|         protected virtual void WriteServiceFooter(GeneratorContext ctx, ServiceDescriptorProto obj, ref object state) { } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Emit code representing a service method | ||||
|         /// </summary> | ||||
|         protected virtual void WriteServiceMethod(GeneratorContext ctx, MethodDescriptorProto inner, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code following preceeding a set of service methods | ||||
|         /// </summary> | ||||
|         protected virtual void WriteServiceHeader(GeneratorContext ctx, ServiceDescriptorProto obj, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Check whether a particular message should be suppressed - for example because it represents a map | ||||
|         /// </summary> | ||||
|         protected virtual bool ShouldOmitMessage(GeneratorContext ctx, DescriptorProto obj, ref object state) | ||||
|             => obj.Options?.MapEntry ?? false; // don't write this type - use a dictionary instead | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Emit code representing a message type | ||||
|         /// </summary> | ||||
|         protected virtual void WriteMessage(GeneratorContext ctx, DescriptorProto obj) | ||||
|         { | ||||
|             object state = null; | ||||
|             if (ShouldOmitMessage(ctx, obj, ref state)) return; | ||||
| 
 | ||||
|             WriteMessageHeader(ctx, obj, ref state); | ||||
|             var oneOfs = OneOfStub.Build(ctx, obj); | ||||
|             foreach (var inner in obj.Fields) | ||||
|             { | ||||
|                 WriteField(ctx, inner, ref state, oneOfs); | ||||
|             } | ||||
|             foreach (var inner in obj.NestedTypes) | ||||
|             { | ||||
|                 WriteMessage(ctx, inner); | ||||
|             } | ||||
|             foreach (var inner in obj.EnumTypes) | ||||
|             { | ||||
|                 WriteEnum(ctx, inner); | ||||
|             } | ||||
|             if (obj.Extensions.Count != 0) | ||||
|             { | ||||
|                 object extState = null; | ||||
|                 WriteExtensionsHeader(ctx, obj, ref extState); | ||||
|                 foreach (var ext in obj.Extensions) | ||||
|                 { | ||||
|                     WriteExtension(ctx, ext); | ||||
|                 } | ||||
|                 WriteExtensionsFooter(ctx, obj, ref extState); | ||||
|             } | ||||
|             WriteMessageFooter(ctx, obj, ref state); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Emit code representing a message field | ||||
|         /// </summary> | ||||
|         protected abstract void WriteField(GeneratorContext ctx, FieldDescriptorProto obj, ref object state, OneOfStub[] oneOfs); | ||||
|         /// <summary> | ||||
|         /// Emit code following a set of message fields | ||||
|         /// </summary> | ||||
|         protected abstract void WriteMessageFooter(GeneratorContext ctx, DescriptorProto obj, ref object state); | ||||
|         /// <summary> | ||||
|         /// Emit code preceeding a set of message fields | ||||
|         /// </summary> | ||||
|         protected abstract void WriteMessageHeader(GeneratorContext ctx, DescriptorProto obj, ref object state); | ||||
|         /// <summary> | ||||
|         /// Emit code representing an enum type | ||||
|         /// </summary> | ||||
|         protected virtual void WriteEnum(GeneratorContext ctx, EnumDescriptorProto obj) | ||||
|         { | ||||
|             object state = null; | ||||
|             WriteEnumHeader(ctx, obj, ref state); | ||||
|             foreach (var inner in obj.Values) | ||||
|             { | ||||
|                 WriteEnumValue(ctx, inner, ref state); | ||||
|             } | ||||
|             WriteEnumFooter(ctx, obj, ref state); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Emit code preceeding a set of enum values | ||||
|         /// </summary> | ||||
|         protected abstract void WriteEnumHeader(GeneratorContext ctx, EnumDescriptorProto obj, ref object state); | ||||
|         /// <summary> | ||||
|         /// Emit code representing an enum value | ||||
|         /// </summary> | ||||
|         protected abstract void WriteEnumValue(GeneratorContext ctx, EnumValueDescriptorProto obj, ref object state); | ||||
|         /// <summary> | ||||
|         /// Emit code following a set of enum values | ||||
|         /// </summary> | ||||
|         protected abstract void WriteEnumFooter(GeneratorContext ctx, EnumDescriptorProto obj, ref object state); | ||||
|         /// <summary> | ||||
|         /// Emit code at the start of a file | ||||
|         /// </summary> | ||||
|         protected virtual void WriteFileHeader(GeneratorContext ctx, FileDescriptorProto obj, ref object state) { } | ||||
|         /// <summary> | ||||
|         /// Emit code at the end of a file | ||||
|         /// </summary> | ||||
|         protected virtual void WriteFileFooter(GeneratorContext ctx, FileDescriptorProto obj, ref object state) { } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Represents the state of a code-generation invocation | ||||
|         /// </summary> | ||||
|         protected class GeneratorContext | ||||
|         { | ||||
|             /// <summary> | ||||
|             /// The file being processed | ||||
|             /// </summary> | ||||
|             public FileDescriptorProto File { get; } | ||||
|             /// <summary> | ||||
|             /// The token to use for indentation | ||||
|             /// </summary> | ||||
|             public string IndentToken { get; } | ||||
|             /// <summary> | ||||
|             /// The current indent level | ||||
|             /// </summary> | ||||
|             public int IndentLevel { get; private set; } | ||||
|             /// <summary> | ||||
|             /// The mechanism to use for name normalization | ||||
|             /// </summary> | ||||
|             public NameNormalizer NameNormalizer { get; } | ||||
|             /// <summary> | ||||
|             /// The output for this code generation | ||||
|             /// </summary> | ||||
|             public TextWriter Output { get; } | ||||
|             /// <summary> | ||||
|             /// The effective syntax of this code-generation cycle, defaulting to "proto2" if not explicity specified | ||||
|             /// </summary> | ||||
|             public string Syntax => string.IsNullOrWhiteSpace(File.Syntax) ? FileDescriptorProto.SyntaxProto2 : File.Syntax; | ||||
|             /// <summary> | ||||
|             /// Create a new GeneratorContext instance | ||||
|             /// </summary> | ||||
|             internal GeneratorContext(FileDescriptorProto file, NameNormalizer nameNormalizer, TextWriter output, string indentToken) | ||||
|             { | ||||
|                 File = file; | ||||
|                 NameNormalizer = nameNormalizer; | ||||
|                 Output = output; | ||||
|                 IndentToken = indentToken; | ||||
|             } | ||||
| 
 | ||||
|             /// <summary> | ||||
|             /// Ends the current line | ||||
|             /// </summary> | ||||
|             public GeneratorContext WriteLine() | ||||
|             { | ||||
|                 Output.WriteLine(); | ||||
|                 return this; | ||||
|             } | ||||
|             /// <summary> | ||||
|             /// Appends a value and ends the current line | ||||
|             /// </summary> | ||||
|             public GeneratorContext WriteLine(string line) | ||||
|             { | ||||
|                 var indentLevel = IndentLevel; | ||||
|                 var target = Output; | ||||
|                 while (indentLevel-- > 0) | ||||
|                 { | ||||
|                     target.Write(IndentToken); | ||||
|                 } | ||||
|                 target.WriteLine(line); | ||||
|                 return this; | ||||
|             } | ||||
|             /// <summary> | ||||
|             /// Appends a value to the current line | ||||
|             /// </summary> | ||||
|             public TextWriter Write(string value) | ||||
|             { | ||||
|                 var indentLevel = IndentLevel; | ||||
|                 var target = Output; | ||||
|                 while (indentLevel-- > 0) | ||||
|                 { | ||||
|                     target.Write(IndentToken); | ||||
|                 } | ||||
|                 target.Write(value); | ||||
|                 return target; | ||||
|             } | ||||
|             /// <summary> | ||||
|             /// Increases the indentation level | ||||
|             /// </summary> | ||||
|             public GeneratorContext Indent() | ||||
|             { | ||||
|                 IndentLevel++; | ||||
|                 return this; | ||||
|             } | ||||
|             /// <summary> | ||||
|             /// Decreases the indentation level | ||||
|             /// </summary> | ||||
|             public GeneratorContext Outdent() | ||||
|             { | ||||
|                 IndentLevel--; | ||||
|                 return this; | ||||
|             } | ||||
| 
 | ||||
|             /// <summary> | ||||
|             /// Try to find a descriptor of the type specified by T with the given full name | ||||
|             /// </summary> | ||||
|             public T TryFind<T>(string typeName) where T : class | ||||
|             { | ||||
|                 object obj; | ||||
|                 if (!_knownTypes.TryGetValue(typeName, out obj) || obj == null) | ||||
|                 { | ||||
|                     return null; | ||||
|                 } | ||||
|                 return obj as T; | ||||
|             } | ||||
| 
 | ||||
|             private Dictionary<string, object> _knownTypes = new Dictionary<string, object>(); | ||||
| 			void AddMessage(DescriptorProto message) | ||||
| 			{ | ||||
| 				_knownTypes[message.FullyQualifiedName] = message; | ||||
| 				foreach (var @enum in message.EnumTypes) | ||||
| 				{ | ||||
| 					_knownTypes[@enum.FullyQualifiedName] = @enum; | ||||
| 				} | ||||
| 				foreach (var msg in message.NestedTypes) | ||||
| 				{ | ||||
| 					AddMessage(msg); | ||||
| 				} | ||||
| 			} | ||||
|             internal void BuildTypeIndex() | ||||
|             { | ||||
|                 { | ||||
|                     var processedFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||||
|                     var pendingFiles = new Queue<FileDescriptorProto>(); | ||||
| 
 | ||||
|                     _knownTypes.Clear(); | ||||
|                     processedFiles.Add(File.Name); | ||||
|                     pendingFiles.Enqueue(File); | ||||
| 
 | ||||
|                     while (pendingFiles.Count != 0) | ||||
|                     { | ||||
|                         var file = pendingFiles.Dequeue(); | ||||
| 
 | ||||
|                         foreach (var @enum in file.EnumTypes) | ||||
|                         { | ||||
|                             _knownTypes[@enum.FullyQualifiedName] = @enum; | ||||
|                         } | ||||
|                         foreach (var msg in file.MessageTypes) | ||||
|                         { | ||||
|                             AddMessage(msg); | ||||
|                         } | ||||
| 
 | ||||
|                         if (file.HasImports()) | ||||
|                         { | ||||
|                             foreach (var import in file.GetImports()) | ||||
|                             { | ||||
|                                 if (processedFiles.Add(import.Path)) | ||||
|                                 { | ||||
|                                     var importFile = file.Parent.GetFile(import.Path); | ||||
|                                     if (importFile != null) pendingFiles.Enqueue(importFile); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b021246de9d37497cb1f97a9ed01772a | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,141 @@ | |||
| // This file was generated by a tool; you should avoid making direct changes. | ||||
| // Consider using 'partial classes' to extend these types | ||||
| // Input: protogen.proto | ||||
| 
 | ||||
| #pragma warning disable CS1591, CS0612, CS3021 | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenFileOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"namespace")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Namespace { get; set; } = ""; | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(2, Name = @"access")] | ||||
|         public Access Access { get; set; } | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(3, Name = @"extensions")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string ExtensionTypeName { get; set; } = ""; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenMessageOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(2, Name = @"access")] | ||||
|         public Access Access { get; set; } | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(3, Name = @"extensions")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string ExtensionTypeName { get; set; } = ""; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenFieldOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(2, Name = @"access")] | ||||
|         public Access Access { get; set; } | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(3, Name = @"asRef")] | ||||
|         public bool AsReference { get; set; } | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(4, Name = @"dynamicType")] | ||||
|         public bool DynamicType { get; set; } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenEnumOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(2, Name = @"access")] | ||||
|         public Access Access { get; set; } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenEnumValueOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenServiceOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|         [global::ProtoBuf.ProtoMember(2, Name = @"access")] | ||||
|         public Access Access { get; set; } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public partial class ProtogenMethodOptions | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoMember(1, Name = @"name")] | ||||
|         [global::System.ComponentModel.DefaultValue("")] | ||||
|         public string Name { get; set; } = ""; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     [global::ProtoBuf.ProtoContract()] | ||||
|     public enum Access | ||||
|     { | ||||
|         [global::ProtoBuf.ProtoEnum(Name = @"INHERIT")] | ||||
|         Inherit = 0, | ||||
|         [global::ProtoBuf.ProtoEnum(Name = @"PUBLIC")] | ||||
|         Public = 1, | ||||
|         [global::ProtoBuf.ProtoEnum(Name = @"PRIVATE")] | ||||
|         Private = 2, | ||||
|         [global::ProtoBuf.ProtoEnum(Name = @"INTERNAL")] | ||||
|         Internal = 3, | ||||
|     } | ||||
| 
 | ||||
|     public static class Extensions | ||||
|     { | ||||
|         public static ProtogenFileOptions GetOptions(this global::Google.Protobuf.Reflection.FileOptions obj) | ||||
|         => obj == null ? default(ProtogenFileOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenFileOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenMessageOptions GetOptions(this global::Google.Protobuf.Reflection.MessageOptions obj) | ||||
|         => obj == null ? default(ProtogenMessageOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenMessageOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenFieldOptions GetOptions(this global::Google.Protobuf.Reflection.FieldOptions obj) | ||||
|         => obj == null ? default(ProtogenFieldOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenFieldOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenEnumOptions GetOptions(this global::Google.Protobuf.Reflection.EnumOptions obj) | ||||
|         => obj == null ? default(ProtogenEnumOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenEnumOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenEnumValueOptions GetOptions(this global::Google.Protobuf.Reflection.EnumValueOptions obj) | ||||
|         => obj == null ? default(ProtogenEnumValueOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenEnumValueOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenServiceOptions GetOptions(this global::Google.Protobuf.Reflection.ServiceOptions obj) | ||||
|         => obj == null ? default(ProtogenServiceOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenServiceOptions>(obj, 1037); | ||||
| 
 | ||||
|         public static ProtogenMethodOptions GetOptions(this global::Google.Protobuf.Reflection.MethodOptions obj) | ||||
|         => obj == null ? default(ProtogenMethodOptions) : global::ProtoBuf.Extensible.GetValue<ProtogenMethodOptions>(obj, 1037); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #pragma warning restore CS1591, CS0612, CS3021 | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: c7a900f1d35ef45bd8dd1a9708204ef2 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a97ff5a986e394a77a730ff22b235b42 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,236 @@ | |||
| using Google.Protobuf.Reflection; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Text.RegularExpressions; | ||||
| 
 | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
| 
 | ||||
|     internal class ParserException : Exception | ||||
|     { | ||||
|         public int ColumnNumber { get; } | ||||
|         public int LineNumber { get; } | ||||
|         public string File { get; } | ||||
|         public string Text { get; } | ||||
|         public string LineContents { get; } | ||||
|         public bool IsError { get; } | ||||
|         internal ParserException(Token token, string message, bool isError) | ||||
|             : base(message ?? "error") | ||||
|         { | ||||
|             ColumnNumber = token.ColumnNumber; | ||||
|             LineNumber = token.LineNumber; | ||||
|             File = token.File; | ||||
|             LineContents = token.LineContents; | ||||
|             Text = token.Value ?? ""; | ||||
|             IsError = isError; | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// Provides general purpose name suggestions | ||||
|     /// </summary> | ||||
|     public abstract class NameNormalizer | ||||
|     { | ||||
|         private class NullNormalizer : NameNormalizer | ||||
|         { | ||||
|             protected override string GetName(string identifier) => identifier; | ||||
|             /// <summary> | ||||
|             /// Suggest a name with idiomatic pluralization | ||||
|             /// </summary> | ||||
|             public override string Pluralize(string identifier) => identifier; | ||||
|         } | ||||
|         private class DefaultNormalizer : NameNormalizer | ||||
|         { | ||||
|             protected override string GetName(string identifier) => AutoCapitalize(identifier); | ||||
|             /// <summary> | ||||
|             /// Suggest a name with idiomatic pluralization | ||||
|             /// </summary> | ||||
|             public override string Pluralize(string identifier) => AutoPluralize(identifier); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a name with idiomatic name capitalization | ||||
|         /// </summary> | ||||
|         public static string AutoCapitalize(string identifier) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(identifier)) return identifier; | ||||
|             // if all upper-case, make proper-case | ||||
|             if (Regex.IsMatch(identifier, @"^[_A-Z0-9]*$")) | ||||
|             { | ||||
|                 return Regex.Replace(identifier, @"(^|_)([A-Z0-9])([A-Z0-9]*)", | ||||
|                     match => match.Groups[2].Value.ToUpperInvariant() + match.Groups[3].Value.ToLowerInvariant()); | ||||
|             } | ||||
|             // if all lower-case, make proper case | ||||
|             if (Regex.IsMatch(identifier, @"^[_a-z0-9]*$")) | ||||
|             { | ||||
|                 return Regex.Replace(identifier, @"(^|_)([a-z0-9])([a-z0-9]*)", | ||||
|                     match => match.Groups[2].Value.ToUpperInvariant() + match.Groups[3].Value.ToLowerInvariant()); | ||||
|             } | ||||
|             // just remove underscores - leave their chosen casing alone | ||||
|             return identifier.Replace("_", ""); | ||||
|         } | ||||
|         public static string AutoCapitalizeFullName(string fullName) | ||||
|         { | ||||
|             var names = fullName.Split('.'); | ||||
|             var s = ""; | ||||
|             for (int i = 0; i < names.Length; i++) | ||||
|             { | ||||
|                 if (i == names.Length - 1) | ||||
|                 { | ||||
|                     s += $"{AutoCapitalize(names[i])}"; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     s += $"{AutoCapitalize(names[i])}."; | ||||
|                 } | ||||
|             } | ||||
|             return s; | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a name with idiomatic pluralization | ||||
|         /// </summary> | ||||
|         protected static string AutoPluralize(string identifier) | ||||
|         { | ||||
|             // horribly Anglo-centric and only covers common cases; but: is swappable | ||||
| 
 | ||||
|             if (string.IsNullOrEmpty(identifier) || identifier.Length == 1) return identifier; | ||||
| 
 | ||||
|             if (identifier.EndsWith("ss") || identifier.EndsWith("o")) return identifier + "es"; | ||||
|             if (identifier.EndsWith("is") && identifier.Length > 2) return identifier.Substring(0, identifier.Length - 2) + "es"; | ||||
| 
 | ||||
|             if (identifier.EndsWith("s")) return identifier; // misses some things (bus => buses), but: might already be pluralized | ||||
| 
 | ||||
|             if (identifier.EndsWith("y") && identifier.Length > 2) | ||||
|             {   // identity => identities etc | ||||
|                 switch (identifier[identifier.Length - 2]) | ||||
|                 { | ||||
|                     case 'a': | ||||
|                     case 'e': | ||||
|                     case 'i': | ||||
|                     case 'o': | ||||
|                     case 'u': | ||||
|                         break; // only for consonant prefix | ||||
|                     default: | ||||
|                         return identifier.Substring(0, identifier.Length - 1) + "ies"; | ||||
|                 } | ||||
|             } | ||||
|             return identifier + "s"; | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Name normalizer with default protobuf-net behaviour, using .NET idioms | ||||
|         /// </summary> | ||||
|         public static NameNormalizer Default { get; } = new DefaultNormalizer(); | ||||
|         /// <summary> | ||||
|         /// Name normalizer that passes through all identifiers without any changes | ||||
|         /// </summary> | ||||
|         public static NameNormalizer Null { get; } = new NullNormalizer(); | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         protected abstract string GetName(string identifier); | ||||
|         /// <summary> | ||||
|         /// Suggest a name with idiomatic pluralization | ||||
|         /// </summary> | ||||
|         public abstract string Pluralize(string identifier); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         public virtual string GetName(FileDescriptorProto definition) | ||||
|         { | ||||
|             var ns = definition?.Options?.GetOptions()?.Namespace; | ||||
|             if (!string.IsNullOrWhiteSpace(ns)) return ns; | ||||
|             ns = definition.Options?.CsharpNamespace; | ||||
|             if (string.IsNullOrWhiteSpace(ns)) ns = GetName(definition.Package); | ||||
| 
 | ||||
|             return string.IsNullOrWhiteSpace(ns) ? null : ns; | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         public virtual string GetName(DescriptorProto definition) | ||||
|         { | ||||
|             var name = definition?.Options?.GetOptions()?.Name; | ||||
|             if (!string.IsNullOrWhiteSpace(name)) return name; | ||||
|             return GetName(definition.Parent as DescriptorProto, GetName(definition.Name), definition.Name, false); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         public virtual string GetName(EnumDescriptorProto definition) | ||||
|         { | ||||
|             var name = definition?.Options?.GetOptions()?.Name; | ||||
|             if (!string.IsNullOrWhiteSpace(name)) return name; | ||||
|             return GetName(definition.Parent as DescriptorProto, GetName(definition.Name), definition.Name, false); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         public virtual string GetName(EnumValueDescriptorProto definition) | ||||
|         { | ||||
|             var name = definition?.Options?.GetOptions()?.Name; | ||||
|             if (!string.IsNullOrWhiteSpace(name)) return name; | ||||
|             return AutoCapitalize(definition.Name); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Suggest a normalized identifier | ||||
|         /// </summary> | ||||
|         public virtual string GetName(FieldDescriptorProto definition) | ||||
|         { | ||||
|             var name = definition?.Options?.GetOptions()?.Name; | ||||
|             if (!string.IsNullOrWhiteSpace(name)) return name; | ||||
|             var preferred = GetName(definition.Name); | ||||
|             if (definition.label == FieldDescriptorProto.Label.LabelRepeated) | ||||
|             { | ||||
|                 preferred = Pluralize(preferred); | ||||
|             } | ||||
|             return GetName(definition.Parent as DescriptorProto, preferred, definition.Name, true); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Obtain a set of all names defined for a message | ||||
|         /// </summary> | ||||
|         protected HashSet<string> BuildConflicts(DescriptorProto parent, bool includeDescendents) | ||||
|         { | ||||
|             var conflicts = new HashSet<string>(); | ||||
|             if (parent != null) | ||||
|             { | ||||
|                 conflicts.Add(GetName(parent)); | ||||
|                 if (includeDescendents) | ||||
|                 { | ||||
|                     foreach (var type in parent.NestedTypes) | ||||
|                     { | ||||
|                         conflicts.Add(GetName(type)); | ||||
|                     } | ||||
|                     foreach (var type in parent.EnumTypes) | ||||
|                     { | ||||
|                         conflicts.Add(GetName(type)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return conflicts; | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Get the preferred name for an element | ||||
|         /// </summary> | ||||
|         protected virtual string GetName(DescriptorProto parent, string preferred, string fallback, bool includeDescendents) | ||||
|         { | ||||
|             var conflicts = BuildConflicts(parent, includeDescendents); | ||||
| 
 | ||||
|             if (!conflicts.Contains(preferred)) return preferred; | ||||
|             if (!conflicts.Contains(fallback)) return fallback; | ||||
| 
 | ||||
|             var attempt = preferred + "Value"; | ||||
|             if (!conflicts.Contains(attempt)) return attempt; | ||||
| 
 | ||||
|             attempt = fallback + "Value"; | ||||
|             if (!conflicts.Contains(attempt)) return attempt; | ||||
| 
 | ||||
|             int i = 1; | ||||
|             while (true) | ||||
|             { | ||||
|                 attempt = preferred + i.ToString(); | ||||
|                 if (!conflicts.Contains(attempt)) return attempt; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: acacb9da00b544a9782cc2e20c7cf13a | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: e55e7fdae8bab4a52bbc61d0bb88cb6f | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,55 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     internal sealed class Peekable<T> : IDisposable | ||||
|     { | ||||
|         public override string ToString() | ||||
|         { | ||||
|             T val; | ||||
|             return Peek(out val) ? (val?.ToString() ?? "(null)") : "(EOF)"; | ||||
|         } | ||||
|         private readonly IEnumerator<T> _iter; | ||||
|         private T _peek, _prev; | ||||
|         private bool _havePeek, _eof; | ||||
|         public Peekable(IEnumerable<T> sequence) | ||||
|         { | ||||
|             _iter = sequence.GetEnumerator(); | ||||
|         } | ||||
|         public T Previous => _prev; | ||||
|         public bool Consume() | ||||
|         { | ||||
|             T val; | ||||
|             bool haveData = _havePeek || Peek(out val); | ||||
|             _prev = _peek; | ||||
|             _havePeek = false;             | ||||
|             return haveData; | ||||
|         } | ||||
|         public bool Peek(out T next) | ||||
|         { | ||||
|             if (!_havePeek) | ||||
|             { | ||||
|                 if (_iter.MoveNext()) | ||||
|                 { | ||||
|                     _prev = _peek; | ||||
|                     _peek = _iter.Current; | ||||
|                     _havePeek = true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _eof = true; | ||||
|                     _havePeek = false; | ||||
|                 } | ||||
|             } | ||||
|             if (_eof) | ||||
|             { | ||||
|                 next = default(T); | ||||
|                 return false; | ||||
|             } | ||||
|             next = _peek; | ||||
|             return true; | ||||
|         } | ||||
|         public void Dispose() => _iter?.Dispose(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 7a3c973520c394370a13670e5eff7ae2 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,83 @@ | |||
| using Google.Protobuf.Reflection; | ||||
| using System; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     internal struct Token | ||||
|     { | ||||
| 
 | ||||
|         public static bool operator ==(Token x, Token y) | ||||
|         { | ||||
|             return x.Offset == y.Offset && x.File == y.File; | ||||
|         } | ||||
|         public static bool operator !=(Token x, Token y) | ||||
|         { | ||||
|             return x.Offset != y.Offset || x.File != y.File; | ||||
|         } | ||||
|         public override int GetHashCode() => Offset; | ||||
|         public override bool Equals(object obj) => (obj is Token) && ((Token)obj).Offset == this.Offset; | ||||
|         public bool Equals(Token token) => token.Offset == this.Offset; | ||||
|         public int Offset { get; } | ||||
|         public int LineNumber { get; } | ||||
|         public string File { get; } | ||||
|         public int ColumnNumber { get; } | ||||
|         public TokenType Type { get; } | ||||
|         public string Value { get; } | ||||
|         public string LineContents { get; } | ||||
|         internal Token(string value, int lineNumber, int columnNumber, TokenType type, string lineContents, int offset, string file) | ||||
|         { | ||||
|             Value = value; | ||||
|             LineNumber = lineNumber; | ||||
|             ColumnNumber = columnNumber; | ||||
|             File = file; | ||||
|             Type = type; | ||||
|             LineContents = lineContents; | ||||
|             Offset = offset; | ||||
|         } | ||||
|         public override string ToString() => $"({LineNumber},{ColumnNumber}) '{Value}'"; | ||||
| 
 | ||||
| 
 | ||||
|         internal Exception Throw(string error = null, bool isError = true) | ||||
|         { | ||||
|             throw new ParserException(this, string.IsNullOrWhiteSpace(error) ? $"syntax error: '{Value}'" : error, isError); | ||||
|         } | ||||
| 
 | ||||
|         internal void Assert(TokenType type, string value = null) | ||||
|         { | ||||
|             if (value != null) | ||||
|             { | ||||
|                 if (type != Type || value != Value) | ||||
|                 { | ||||
|                     Throw($"expected {type} '{value}'"); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (type != Type) | ||||
|                 { | ||||
|                     Throw($"expected {type}"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal bool Is(TokenType type, string value = null) | ||||
|         { | ||||
|             if (type != Type) return false; | ||||
|             if (value != null && value != Value) return false; | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         internal void RequireProto2(ParserContext ctx) | ||||
|         { | ||||
|             if(ctx.Syntax != FileDescriptorProto.SyntaxProto2) | ||||
|             { | ||||
|                 var msg = "'" + Value + "' requires " + FileDescriptorProto.SyntaxProto2 + " syntax"; | ||||
|                 ctx.Errors.Error(this, msg); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal Error TypeNotFound(string typeName = null) => new Error(this, | ||||
|             $"type not found: '{(string.IsNullOrWhiteSpace(typeName) ? Value : typeName)}'", true); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: aea55379c2cfd4ea8a17e19afd57938c | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,642 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Globalization; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Reflection; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     internal static class TokenExtensions | ||||
|     { | ||||
|         public static bool Is(this Peekable<Token> tokens, TokenType type, string value = null) | ||||
|         { | ||||
|             Token val;  | ||||
|             return tokens.Peek(out val) && val.Is(type, value); | ||||
|         } | ||||
| 
 | ||||
|         public static void Consume(this Peekable<Token> tokens, TokenType type, string value) | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             token.Assert(type, value); | ||||
|             tokens.Consume(); | ||||
|         } | ||||
|         public static bool ConsumeIf(this Peekable<Token> tokens, TokenType type, string value) | ||||
|         { | ||||
|             Token token; | ||||
|             if (tokens.Peek(out token) && token.Is(type, value)) | ||||
|             { | ||||
|                 tokens.Consume(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         public static Token Read(this Peekable<Token> tokens) | ||||
|         { | ||||
|             Token val; | ||||
|             if (!tokens.Peek(out val)) | ||||
|             { | ||||
|                 throw new ParserException(tokens.Previous, "Unexpected end of file", true); | ||||
|             } | ||||
|             return val; | ||||
|         } | ||||
|         public static bool SkipToEndOptions(this Peekable<Token> tokens) | ||||
|         { | ||||
|             Token token; | ||||
|             while (tokens.Peek(out token)) | ||||
|             { | ||||
|                 if (token.Is(TokenType.Symbol, ";") || token.Is(TokenType.Symbol, "}")) | ||||
|                     return true; // but don't consume | ||||
| 
 | ||||
|                 tokens.Consume(); | ||||
|                 if (token.Is(TokenType.Symbol, "]")) | ||||
|                     return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         public static bool SkipToEndStatement(this Peekable<Token> tokens) | ||||
|         { | ||||
|             Token token;  | ||||
|             while (tokens.Peek(out token)) | ||||
|             { | ||||
|                 if (token.Is(TokenType.Symbol, "}")) | ||||
|                     return true; // but don't consume | ||||
| 
 | ||||
|                 tokens.Consume(); | ||||
|                 if (token.Is(TokenType.Symbol, ";")) | ||||
|                     return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         public static bool SkipToEndObject(this Peekable<Token> tokens) => SkipToSymbol(tokens, "}"); | ||||
|         private static bool SkipToSymbol(this Peekable<Token> tokens, string symbol) | ||||
|         { | ||||
|             Token token; | ||||
|             while (tokens.Peek(out token)) | ||||
|             { | ||||
|                 tokens.Consume(); | ||||
|                 if (token.Is(TokenType.Symbol, symbol)) | ||||
|                     return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         public static bool SkipToEndStatementOrObject(this Peekable<Token> tokens) | ||||
|         { | ||||
|             Token token; | ||||
|             while (tokens.Peek(out token)) | ||||
|             { | ||||
|                 tokens.Consume(); | ||||
|                 if (token.Is(TokenType.Symbol, "}") || token.Is(TokenType.Symbol, ";")) | ||||
|                     return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         public static string Consume(this Peekable<Token> tokens, TokenType type) | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             token.Assert(type); | ||||
|             string s = token.Value; | ||||
|             tokens.Consume(); | ||||
|             return s; | ||||
|         } | ||||
| 
 | ||||
|         static class EnumCache<T> | ||||
|         { | ||||
|             private static readonly Dictionary<string, T> lookup; | ||||
|             public static bool TryGet(string name, out T value) => lookup.TryGetValue(name, out value); | ||||
|             static EnumCache() | ||||
|             { | ||||
|                 var fields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public); | ||||
|                 var tmp = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); | ||||
|                 foreach (var field in fields) | ||||
|                 { | ||||
|                     string name = field.Name; | ||||
|                     var attrib = (ProtoEnumAttribute)field.GetCustomAttributes(false).FirstOrDefault(); | ||||
|                     if (!string.IsNullOrWhiteSpace(attrib?.Name)) name = attrib.Name; | ||||
|                     var val = (T)field.GetValue(null); | ||||
|                     tmp.Add(name, val); | ||||
|                 } | ||||
|                 lookup = tmp; | ||||
|             } | ||||
|         } | ||||
|         internal static T ConsumeEnum<T>(this Peekable<Token> tokens, bool ignoreCase = true) where T : struct | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             var value = tokens.ConsumeString(); | ||||
| 
 | ||||
|             T val; | ||||
|             if (!EnumCache<T>.TryGet(token.Value, out val)) | ||||
|                 token.Throw("Unable to parse " + typeof(T).Name); | ||||
|             return val; | ||||
|         } | ||||
|         internal static bool TryParseUInt32(string token, out uint val, uint? max = null) | ||||
|         { | ||||
|             if (max.HasValue && token == "max") | ||||
|             { | ||||
|                 val = max.GetValueOrDefault(); | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("0x", StringComparison.OrdinalIgnoreCase) && uint.TryParse(token.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return uint.TryParse(token, NumberStyles.Integer | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|         internal static bool TryParseUInt64(string token, out ulong val, ulong? max = null) | ||||
|         { | ||||
|             if (max.HasValue && token == "max") | ||||
|             { | ||||
|                 val = max.GetValueOrDefault(); | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("0x", StringComparison.OrdinalIgnoreCase) && ulong.TryParse(token.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return ulong.TryParse(token, NumberStyles.Integer | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|         internal static bool TryParseInt32(string token, out int val, int? max = null) | ||||
|         { | ||||
|             if (max.HasValue && token == "max") | ||||
|             { | ||||
|                 val = max.GetValueOrDefault(); | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("-0x", StringComparison.OrdinalIgnoreCase) && int.TryParse(token.Substring(3), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 val = -val; | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("0x", StringComparison.OrdinalIgnoreCase) && int.TryParse(token.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return int.TryParse(token, NumberStyles.Integer | NumberStyles.AllowLeadingSign | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|         internal static bool TryParseInt64(string token, out long val, long? max = null) | ||||
|         { | ||||
|             if (max.HasValue && token == "max") | ||||
|             { | ||||
|                 val = max.GetValueOrDefault(); | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("-0x", StringComparison.OrdinalIgnoreCase) && long.TryParse(token.Substring(3), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 val = -val; | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             if (token.StartsWith("0x", StringComparison.OrdinalIgnoreCase) && long.TryParse(token.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out val)) | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return long.TryParse(token, NumberStyles.Integer | NumberStyles.AllowLeadingSign | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|         internal static int ConsumeInt32(this Peekable<Token> tokens, int? max = null) | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             token.Assert(TokenType.AlphaNumeric); | ||||
|             tokens.Consume(); | ||||
|             int val; | ||||
|             if (TryParseInt32(token.Value, out val, max)) return val; | ||||
|             throw token.Throw("Unable to parse integer"); | ||||
|         } | ||||
| 
 | ||||
|         internal static string ConsumeString(this Peekable<Token> tokens, bool asBytes = false) | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             switch (token.Type) | ||||
|             { | ||||
|                 case TokenType.StringLiteral: | ||||
|                     MemoryStream ms = null; | ||||
|                     do | ||||
|                     { | ||||
|                         ReadStringBytes(ref ms, token.Value); | ||||
|                         tokens.Consume(); | ||||
|                     } while (tokens.Peek(out token) && token.Type == TokenType.StringLiteral); // literal concat is a thing | ||||
|                     if (ms == null) return ""; | ||||
| 
 | ||||
|                     if (!asBytes) | ||||
|                     { | ||||
| #if NETSTANDARD1_3 | ||||
|                         string s = ms.TryGetBuffer(out var segment) | ||||
|                             ? Encoding.UTF8.GetString(segment.Array, segment.Offset, segment.Count) | ||||
|                             : Encoding.UTF8.GetString(ms.ToArray()); | ||||
| 
 | ||||
| #else | ||||
|                         string s = Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); | ||||
| #endif | ||||
|                         return s.Replace("\\", @"\\") | ||||
|                             .Replace("\'", @"\'") | ||||
|                             .Replace("\"", @"\""") | ||||
|                             .Replace("\r", @"\r") | ||||
|                             .Replace("\n", @"\n") | ||||
|                             .Replace("\t", @"\t"); | ||||
|                     } | ||||
| 
 | ||||
|                     var sb = new StringBuilder((int)ms.Length); | ||||
|                     int b; | ||||
|                     ms.Position = 0; | ||||
|                     while ((b = ms.ReadByte()) >= 0) | ||||
|                     { | ||||
|                         switch (b) | ||||
|                         { | ||||
|                             case '\n': sb.Append(@"\n"); break; | ||||
|                             case '\r': sb.Append(@"\r"); break; | ||||
|                             case '\t': sb.Append(@"\t"); break; | ||||
|                             case '\'': sb.Append(@"\'"); break; | ||||
|                             case '\"': sb.Append(@"\"""); break; | ||||
|                             case '\\': sb.Append(@"\\"); break; | ||||
|                             default: | ||||
|                                 if (b >= 32 && b < 127) | ||||
|                                 { | ||||
|                                     sb.Append((char)b); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     // encode as 3-part octal | ||||
|                                     sb.Append('\\') | ||||
|                                           .Append((char)(((b >> 6) & 7) + (int)'0')) | ||||
|                                           .Append((char)(((b >> 3) & 7) + (int)'0')) | ||||
|                                           .Append((char)(((b >> 0) & 7) + (int)'0')); | ||||
|                                 } | ||||
|                                 break; | ||||
|                         } | ||||
|                     } | ||||
|                     return sb.ToString(); | ||||
|                 case TokenType.AlphaNumeric: | ||||
|                     tokens.Consume(); | ||||
|                     return token.Value; | ||||
|                 default: | ||||
|                     throw token.Throw(); | ||||
|             } | ||||
|         } | ||||
| 		internal static void AppendAscii(MemoryStream target, string ascii) | ||||
| 		{ | ||||
| 			foreach (char c in ascii) | ||||
| 				target.WriteByte(checked((byte)c)); | ||||
| 		} | ||||
| 		internal static void AppendByte(MemoryStream target, ref uint codePoint, ref int len) | ||||
| 		{ | ||||
| 			if (len != 0) | ||||
| 			{ | ||||
| 				target.WriteByte(checked((byte)codePoint)); | ||||
| 			} | ||||
| 			codePoint = 0; | ||||
| 			len = 0; | ||||
| 		} | ||||
| 		internal static unsafe void AppendNormalized(MemoryStream target, ref uint codePoint, ref int len) | ||||
| 		{ | ||||
| 			if (len == 0) | ||||
| 			{ | ||||
| 				codePoint = 0; | ||||
| 				return; | ||||
| 			} | ||||
| 			byte* b = stackalloc byte[10]; | ||||
| 			char c = checked((char)codePoint); | ||||
| 			int count = Encoding.UTF8.GetBytes(&c, 1, b, 10); | ||||
| 			for (int i = 0; i < count; i++) | ||||
| 			{ | ||||
| 				target.WriteByte(b[i]); | ||||
| 			} | ||||
| 		} | ||||
| 		internal static void AppendEscaped(MemoryStream target, char c) | ||||
| 		{ | ||||
| 			uint codePoint; | ||||
| 			switch (c) | ||||
| 			{ | ||||
| 				// encoded as octal | ||||
| 				case 'a': codePoint = '\a'; break; | ||||
| 				case 'b': codePoint = '\b'; break; | ||||
| 				case 'f': codePoint = '\f'; break; | ||||
| 				case 'v': codePoint = '\v'; break; | ||||
| 				case 't': codePoint = '\t'; break; | ||||
| 				case 'n': codePoint = '\n'; break; | ||||
| 				case 'r': codePoint = '\r'; break; | ||||
| 
 | ||||
| 				case '\\': | ||||
| 				case '?': | ||||
| 				case '\'': | ||||
| 				case '\"': | ||||
| 					codePoint = c; | ||||
| 					break; | ||||
| 				default: | ||||
| 					codePoint = '?'; | ||||
| 					break; | ||||
| 			} | ||||
| 			int len = 1; | ||||
| 			AppendNormalized(target, ref codePoint, ref len); | ||||
| 		} | ||||
| 		internal static bool GetHexValue(char c, out uint val, ref int len) | ||||
| 		{ | ||||
| 			len++; | ||||
| 			if (c >= '0' && c <= '9') | ||||
| 			{ | ||||
| 				val = (uint)c - (uint)'0'; | ||||
| 				return true; | ||||
| 			} | ||||
| 			if (c >= 'a' && c <= 'f') | ||||
| 			{ | ||||
| 				val = 10 + (uint)c - (uint)'a'; | ||||
| 				return true; | ||||
| 			} | ||||
| 			if (c >= 'A' && c <= 'F') | ||||
| 			{ | ||||
| 				val = 10 + (uint)c - (uint)'A'; | ||||
| 				return true; | ||||
| 			} | ||||
| 			len--; | ||||
| 			val = 0; | ||||
| 			return false; | ||||
| 		} | ||||
|         // the normalized output *includes* the slashes, but expands octal to 3 places; | ||||
|         // it is the job of codegen to change this normalized form to the target language form | ||||
|         internal static void ReadStringBytes(ref MemoryStream ms, string value) | ||||
|         { | ||||
|             const int STATE_NORMAL = 0, STATE_ESCAPE = 1, STATE_OCTAL = 2, STATE_HEX = 3; | ||||
|             int state = STATE_NORMAL; | ||||
|             if (value == null || value.Length == 0) return; | ||||
| 
 | ||||
|             if (ms == null) ms = new MemoryStream(value.Length); | ||||
|             uint escapedCodePoint = 0; | ||||
|             int escapeLength = 0; | ||||
|             foreach (char c in value) | ||||
|             { | ||||
|                 switch (state) | ||||
|                 { | ||||
|                     case STATE_ESCAPE: | ||||
|                         if (c >= '0' && c <= '7') | ||||
|                         { | ||||
|                             state = STATE_OCTAL; | ||||
|                             GetHexValue(c, out escapedCodePoint, ref escapeLength); // not a typo; all 1-char octal values are also the same in hex | ||||
|                         } | ||||
|                         else if (c == 'x') | ||||
|                         { | ||||
|                             state = STATE_HEX; | ||||
|                         } | ||||
|                         else if (c == 'u' || c == 'U') | ||||
|                         { | ||||
|                             throw new NotSupportedException("Unicode escape points: on my todo list"); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             state = STATE_NORMAL; | ||||
|                             AppendEscaped(ms, c); | ||||
|                         } | ||||
|                         break; | ||||
|                     case STATE_OCTAL: | ||||
|                         if (c >= '0' && c <= '7') | ||||
|                         { | ||||
|                             uint x; | ||||
|                             GetHexValue(c, out x, ref escapeLength); | ||||
|                             escapedCodePoint = (escapedCodePoint << 3) | x; | ||||
|                             if (escapeLength == 3) | ||||
|                             { | ||||
|                                 AppendByte(ms, ref escapedCodePoint, ref escapeLength); | ||||
|                                 state = STATE_NORMAL; | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             // not an octal char - regular append | ||||
|                             if (escapeLength == 0) | ||||
|                             { | ||||
|                                 // include the malformed \x | ||||
|                                 AppendAscii(ms, @"\x"); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 AppendByte(ms, ref escapedCodePoint, ref escapeLength); | ||||
|                             } | ||||
|                             state = STATE_NORMAL; | ||||
|                             goto case STATE_NORMAL; | ||||
|                         } | ||||
|                         break; | ||||
|                     case STATE_HEX: | ||||
|                         { | ||||
|                             uint x; | ||||
|                             if (GetHexValue(c, out x, ref escapeLength)) | ||||
|                             { | ||||
|                                 escapedCodePoint = (escapedCodePoint << 4) | x; | ||||
|                                 if (escapeLength == 2) | ||||
|                                 { | ||||
|                                     AppendByte(ms, ref escapedCodePoint, ref escapeLength); | ||||
|                                     state = STATE_NORMAL; | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 // not a hex char - regular append | ||||
|                                 AppendByte(ms, ref escapedCodePoint, ref escapeLength); | ||||
|                                 state = STATE_NORMAL; | ||||
|                                 goto case STATE_NORMAL; | ||||
|                             } | ||||
|                         } | ||||
|                         break; | ||||
|                     case STATE_NORMAL: | ||||
|                         if (c == '\\') | ||||
|                         { | ||||
|                             state = STATE_ESCAPE; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             uint codePoint = (uint)c; | ||||
|                             int len = 1; | ||||
|                             AppendNormalized(ms, ref codePoint, ref len); | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         throw new InvalidOperationException(); | ||||
|                 } | ||||
|             } | ||||
|             // append any trailing escaped data | ||||
|             AppendByte(ms, ref escapedCodePoint, ref escapeLength); | ||||
|         } | ||||
| 
 | ||||
|         internal static bool ConsumeBoolean(this Peekable<Token> tokens) | ||||
|         { | ||||
|             var token = tokens.Read(); | ||||
|             token.Assert(TokenType.AlphaNumeric); | ||||
|             tokens.Consume(); | ||||
|             if (string.Equals("true", token.Value, StringComparison.OrdinalIgnoreCase)) return true; | ||||
|             if (string.Equals("false", token.Value, StringComparison.OrdinalIgnoreCase)) return false; | ||||
|             throw token.Throw("Unable to parse boolean"); | ||||
|         } | ||||
| 
 | ||||
|         static TokenType Identify(char c) | ||||
|         { | ||||
|             if (c == '"' || c == '\'') return TokenType.StringLiteral; | ||||
|             if (char.IsWhiteSpace(c)) return TokenType.Whitespace; | ||||
|             if (char.IsLetterOrDigit(c)) return TokenType.AlphaNumeric; | ||||
|             switch (c) | ||||
|             { | ||||
|                 case '_': | ||||
|                 case '.': | ||||
|                 case '-': | ||||
|                     return TokenType.AlphaNumeric; | ||||
|             } | ||||
|             return TokenType.Symbol; | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<Token> RemoveCommentsAndWhitespace(this IEnumerable<Token> tokens) | ||||
|         { | ||||
|             int commentLineNumber = -1; | ||||
|             bool isBlockComment = false; | ||||
|             foreach (var token in tokens) | ||||
|             { | ||||
|                 if (isBlockComment) | ||||
|                 { | ||||
|                     // swallow everything until the end of the block comment | ||||
|                     if (token.Is(TokenType.Symbol, "*/")) | ||||
|                         isBlockComment = false; | ||||
|                 } | ||||
|                 else if (commentLineNumber == token.LineNumber) | ||||
|                 { | ||||
|                     // swallow everything else on that line | ||||
|                 } | ||||
|                 else if (token.Is(TokenType.Whitespace)) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
|                 else if (token.Is(TokenType.Symbol, "//")) | ||||
|                 { | ||||
|                     commentLineNumber = token.LineNumber; | ||||
|                 } | ||||
|                 else if (token.Is(TokenType.Symbol, "/*")) | ||||
|                 { | ||||
|                     isBlockComment = true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     yield return token; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static bool CanCombine(TokenType type, int len, char prev, char next) | ||||
|             => type != TokenType.Symbol | ||||
|             || (len == 1 && prev == '/' && (next == '/' || next == '*')) | ||||
|             || (len == 1 && prev == '*' && next == '/'); | ||||
| 
 | ||||
| 
 | ||||
|         public static IEnumerable<Token> Tokenize(this TextReader reader, string file) | ||||
|         { | ||||
|             var buffer = new StringBuilder(); | ||||
| 
 | ||||
|             int lineNumber = 0, offset = 0; | ||||
|             string line; | ||||
|             string lastLine = null; | ||||
|             while ((line = reader.ReadLine()) != null) | ||||
|             { | ||||
|                 lastLine = line; | ||||
|                 lineNumber++; | ||||
|                 int columnNumber = 0, tokenStart = 1; | ||||
|                 char lastChar = '\0', stringType = '\0'; | ||||
|                 TokenType type = TokenType.None; | ||||
|                 bool isEscaped = false; | ||||
|                 foreach (char c in line) | ||||
|                 { | ||||
|                     columnNumber++; | ||||
|                     if (type == TokenType.StringLiteral) | ||||
|                     { | ||||
|                         if (c == stringType && !isEscaped) | ||||
|                         { | ||||
|                             yield return new Token(buffer.ToString(), lineNumber, tokenStart, type, line, offset++, file); | ||||
|                             buffer.Clear(); | ||||
|                             type = TokenType.None; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             buffer.Append(c); | ||||
|                             isEscaped = !isEscaped && c == '\\'; // ends an existing escape or starts a new one | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         var newType = Identify(c); | ||||
|                         if (newType == type && CanCombine(type, buffer.Length, lastChar, c)) | ||||
|                         { | ||||
|                             buffer.Append(c); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             if (buffer.Length != 0) | ||||
|                             { | ||||
|                                 yield return new Token(buffer.ToString(), lineNumber, tokenStart, type, line, offset++, file); | ||||
|                                 buffer.Clear(); | ||||
|                             } | ||||
|                             type = newType; | ||||
|                             tokenStart = columnNumber; | ||||
|                             if (newType == TokenType.StringLiteral) | ||||
|                             { | ||||
|                                 stringType = c; | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 buffer.Append(c); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     lastChar = c; | ||||
|                 } | ||||
| 
 | ||||
|                 if (buffer.Length != 0) | ||||
|                 { | ||||
|                     yield return new Token(buffer.ToString(), lineNumber, tokenStart, type, lastLine, offset++, file); | ||||
|                     buffer.Clear(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         internal static bool TryParseSingle(string token, out float val) | ||||
|         { | ||||
|             if (token == "nan") | ||||
|             { | ||||
|                 val = float.NaN; | ||||
|                 return true; | ||||
|             } | ||||
|             if (token == "inf") | ||||
|             { | ||||
|                 val = float.PositiveInfinity; | ||||
|                 return true; | ||||
|             } | ||||
|             if (token == "-inf") | ||||
|             { | ||||
|                 val = float.NegativeInfinity; | ||||
|                 return true; | ||||
|             } | ||||
|             return float.TryParse(token, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|         internal static bool TryParseDouble(string token, out double val) | ||||
|         { | ||||
|             if(token == "nan") | ||||
|             { | ||||
|                 val = double.NaN; | ||||
|                 return true; | ||||
|             } | ||||
|             if(token == "inf") | ||||
|             { | ||||
|                 val = double.PositiveInfinity; | ||||
|                 return true; | ||||
|             } | ||||
|             if(token == "-inf") | ||||
|             { | ||||
|                 val = double.NegativeInfinity; | ||||
|                 return true; | ||||
|             } | ||||
|             return double.TryParse(token, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out val); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 08776b0940a8d42c080f7880619608c4 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,11 @@ | |||
| namespace ProtoBuf.Reflection | ||||
| { | ||||
|     internal enum TokenType | ||||
|     { | ||||
|         None, | ||||
|         Whitespace, | ||||
|         StringLiteral, | ||||
|         AlphaNumeric, | ||||
|         Symbol | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: c55473354eb3c4e338593d7b041132be | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -10,6 +10,10 @@ KCP是一个基于udp的快速可靠协议(rudp),能以比 TCP浪费10%-20%的 | |||
| 
 | ||||
| 主要用于构建unity客户端网络层. | ||||
| 
 | ||||
| kcp库基于https://github.com/l42111996/csharp-kcp | ||||
| protobuf-net库基于https://github.com/protobuf-net/protobuf-net | ||||
| 两个库均做了适当修改,以完美适配Unity | ||||
| 
 | ||||
| 
 | ||||
| ## 安装和接入 | ||||
| 
 | ||||
|  | @ -3,10 +3,8 @@ | |||
|     "rootNamespace": "Guru", | ||||
|     "references": [], | ||||
|     "includePlatforms": [], | ||||
|     "excludePlatforms": [ | ||||
|         "Editor" | ||||
|     ], | ||||
|     "allowUnsafeCode": false, | ||||
|     "excludePlatforms": [], | ||||
|     "allowUnsafeCode": true, | ||||
|     "overrideReferences": false, | ||||
|     "precompiledReferences": [], | ||||
|     "autoReferenced": true, | ||||
|  | @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 22deed51a0f6d4236a6ae1960d426139 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,712 @@ | |||
| using System; | ||||
| using System.Reflection; | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     internal enum TimeSpanScale | ||||
|     { | ||||
|         Days = 0, | ||||
|         Hours = 1, | ||||
|         Minutes = 2, | ||||
|         Seconds = 3, | ||||
|         Milliseconds = 4, | ||||
|         Ticks = 5, | ||||
| 
 | ||||
|         MinMax = 15 | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Provides support for common .NET types that do not have a direct representation | ||||
|     /// in protobuf, using the definitions from bcl.proto | ||||
|     /// </summary> | ||||
|     public static class BclHelpers | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Creates a new instance of the specified type, bypassing the constructor. | ||||
|         /// </summary> | ||||
|         /// <param name="type">The type to create</param> | ||||
|         /// <returns>The new instance</returns> | ||||
|         /// <exception cref="NotSupportedException">If the platform does not support constructor-skipping</exception> | ||||
|         public static object GetUninitializedObject(Type type) | ||||
|         { | ||||
| #if COREFX | ||||
|             object obj = TryGetUninitializedObjectWithFormatterServices(type); | ||||
|             if (obj != null) return obj; | ||||
| #endif | ||||
| #if PLAT_BINARYFORMATTER && !(COREFX || PROFILE259) | ||||
|             return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type); | ||||
| #else | ||||
|             throw new NotSupportedException("Constructor-skipping is not supported on this platform"); | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
| #if COREFX // this is inspired by DCS: https://github.com/dotnet/corefx/blob/c02d33b18398199f6acc17d375dab154e9a1df66/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs#L854-L894 | ||||
|         static Func<Type, object> getUninitializedObject; | ||||
|         static internal object TryGetUninitializedObjectWithFormatterServices(Type type) | ||||
|         { | ||||
|             if (getUninitializedObject == null) | ||||
|             { | ||||
|                 try { | ||||
|                     var formatterServiceType = typeof(string).GetTypeInfo().Assembly.GetType("System.Runtime.Serialization.FormatterServices"); | ||||
|                     if (formatterServiceType == null) | ||||
|                     { | ||||
|                         // fallback for .Net Core 3.0 | ||||
|                         var formatterAssembly = Assembly.Load(new AssemblyName("System.Runtime.Serialization.Formatters")); | ||||
|                         formatterServiceType = formatterAssembly.GetType("System.Runtime.Serialization.FormatterServices"); | ||||
|                     } | ||||
|                     MethodInfo method = formatterServiceType?.GetMethod("GetUninitializedObject", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); | ||||
|                     if (method != null) | ||||
|                     { | ||||
|                         getUninitializedObject = (Func<Type, object>)method.CreateDelegate(typeof(Func<Type, object>)); | ||||
|                     } | ||||
|                 } | ||||
|                 catch  { /* best efforts only */ } | ||||
|                 if(getUninitializedObject == null) getUninitializedObject = x => null; | ||||
|             } | ||||
|             return getUninitializedObject(type); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         const int FieldTimeSpanValue = 0x01, FieldTimeSpanScale = 0x02, FieldTimeSpanKind = 0x03; | ||||
| 
 | ||||
|         internal static readonly DateTime[] EpochOrigin = { | ||||
|             new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), | ||||
|             new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), | ||||
|             new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Local) | ||||
|         }; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The default value for dates that are following google.protobuf.Timestamp semantics | ||||
|         /// </summary> | ||||
|         private static readonly DateTime TimestampEpoch = EpochOrigin[(int)DateTimeKind.Utc]; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a TimeSpan to a protobuf stream using protobuf-net's own representation, bcl.TimeSpan | ||||
|         /// </summary> | ||||
|         public static void WriteTimeSpan(TimeSpan timeSpan, ProtoWriter dest) | ||||
|         { | ||||
|             WriteTimeSpanImpl(timeSpan, dest, DateTimeKind.Unspecified); | ||||
|         } | ||||
| 
 | ||||
|         private static void WriteTimeSpanImpl(TimeSpan timeSpan, ProtoWriter dest, DateTimeKind kind) | ||||
|         { | ||||
|             if (dest == null) throw new ArgumentNullException(nameof(dest)); | ||||
|             long value; | ||||
|             switch (dest.WireType) | ||||
|             { | ||||
|                 case WireType.String: | ||||
|                 case WireType.StartGroup: | ||||
|                     TimeSpanScale scale; | ||||
|                     value = timeSpan.Ticks; | ||||
|                     if (timeSpan == TimeSpan.MaxValue) | ||||
|                     { | ||||
|                         value = 1; | ||||
|                         scale = TimeSpanScale.MinMax; | ||||
|                     } | ||||
|                     else if (timeSpan == TimeSpan.MinValue) | ||||
|                     { | ||||
|                         value = -1; | ||||
|                         scale = TimeSpanScale.MinMax; | ||||
|                     } | ||||
|                     else if (value % TimeSpan.TicksPerDay == 0) | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Days; | ||||
|                         value /= TimeSpan.TicksPerDay; | ||||
|                     } | ||||
|                     else if (value % TimeSpan.TicksPerHour == 0) | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Hours; | ||||
|                         value /= TimeSpan.TicksPerHour; | ||||
|                     } | ||||
|                     else if (value % TimeSpan.TicksPerMinute == 0) | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Minutes; | ||||
|                         value /= TimeSpan.TicksPerMinute; | ||||
|                     } | ||||
|                     else if (value % TimeSpan.TicksPerSecond == 0) | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Seconds; | ||||
|                         value /= TimeSpan.TicksPerSecond; | ||||
|                     } | ||||
|                     else if (value % TimeSpan.TicksPerMillisecond == 0) | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Milliseconds; | ||||
|                         value /= TimeSpan.TicksPerMillisecond; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         scale = TimeSpanScale.Ticks; | ||||
|                     } | ||||
| 
 | ||||
|                     SubItemToken token = ProtoWriter.StartSubItem(null, dest); | ||||
| 
 | ||||
|                     if (value != 0) | ||||
|                     { | ||||
|                         ProtoWriter.WriteFieldHeader(FieldTimeSpanValue, WireType.SignedVariant, dest); | ||||
|                         ProtoWriter.WriteInt64(value, dest); | ||||
|                     } | ||||
|                     if (scale != TimeSpanScale.Days) | ||||
|                     { | ||||
|                         ProtoWriter.WriteFieldHeader(FieldTimeSpanScale, WireType.Variant, dest); | ||||
|                         ProtoWriter.WriteInt32((int)scale, dest); | ||||
|                     } | ||||
|                     if (kind != DateTimeKind.Unspecified) | ||||
|                     { | ||||
|                         ProtoWriter.WriteFieldHeader(FieldTimeSpanKind, WireType.Variant, dest); | ||||
|                         ProtoWriter.WriteInt32((int)kind, dest); | ||||
|                     } | ||||
|                     ProtoWriter.EndSubItem(token, dest); | ||||
|                     break; | ||||
|                 case WireType.Fixed64: | ||||
|                     ProtoWriter.WriteInt64(timeSpan.Ticks, dest); | ||||
|                     break; | ||||
|                 default: | ||||
|                     throw new ProtoException("Unexpected wire-type: " + dest.WireType.ToString()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Parses a TimeSpan from a protobuf stream using protobuf-net's own representation, bcl.TimeSpan | ||||
|         /// </summary>         | ||||
|         public static TimeSpan ReadTimeSpan(ProtoReader source) | ||||
|         { | ||||
|             long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind); | ||||
|             if (ticks == long.MinValue) return TimeSpan.MinValue; | ||||
|             if (ticks == long.MaxValue) return TimeSpan.MaxValue; | ||||
|             return TimeSpan.FromTicks(ticks); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Parses a TimeSpan from a protobuf stream using the standardized format, google.protobuf.Duration | ||||
|         /// </summary> | ||||
|         public static TimeSpan ReadDuration(ProtoReader source) | ||||
|         { | ||||
|             long seconds = 0; | ||||
|             int nanos = 0; | ||||
|             SubItemToken token = ProtoReader.StartSubItem(source); | ||||
|             int fieldNumber; | ||||
|             while ((fieldNumber = source.ReadFieldHeader()) > 0) | ||||
|             { | ||||
|                 switch (fieldNumber) | ||||
|                 { | ||||
|                     case 1: | ||||
|                         seconds = source.ReadInt64(); | ||||
|                         break; | ||||
|                     case 2: | ||||
|                         nanos = source.ReadInt32(); | ||||
|                         break; | ||||
|                     default: | ||||
|                         source.SkipField(); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|             ProtoReader.EndSubItem(token, source); | ||||
|             return FromDurationSeconds(seconds, nanos); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a TimeSpan to a protobuf stream using the standardized format, google.protobuf.Duration | ||||
|         /// </summary> | ||||
|         public static void WriteDuration(TimeSpan value, ProtoWriter dest) | ||||
|         { | ||||
|             var seconds = ToDurationSeconds(value, out int nanos); | ||||
|             WriteSecondsNanos(seconds, nanos, dest); | ||||
|         } | ||||
| 
 | ||||
|         private static void WriteSecondsNanos(long seconds, int nanos, ProtoWriter dest) | ||||
|         { | ||||
|             SubItemToken token = ProtoWriter.StartSubItem(null, dest); | ||||
|             if (seconds != 0) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(1, WireType.Variant, dest); | ||||
|                 ProtoWriter.WriteInt64(seconds, dest); | ||||
|             } | ||||
|             if (nanos != 0) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(2, WireType.Variant, dest); | ||||
|                 ProtoWriter.WriteInt32(nanos, dest); | ||||
|             } | ||||
|             ProtoWriter.EndSubItem(token, dest); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Parses a DateTime from a protobuf stream using the standardized format, google.protobuf.Timestamp | ||||
|         /// </summary> | ||||
|         public static DateTime ReadTimestamp(ProtoReader source) | ||||
|         { | ||||
|             // note: DateTime is only defined for just over 0000 to just below 10000; | ||||
|             // TimeSpan has a range of +/- 10,675,199 days === 29k years; | ||||
|             // so we can just use epoch time delta | ||||
|             return TimestampEpoch + ReadDuration(source); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a DateTime to a protobuf stream using the standardized format, google.protobuf.Timestamp | ||||
|         /// </summary> | ||||
|         public static void WriteTimestamp(DateTime value, ProtoWriter dest) | ||||
|         { | ||||
|             var seconds = ToDurationSeconds(value - TimestampEpoch, out int nanos); | ||||
| 
 | ||||
|             if (nanos < 0) | ||||
|             {   // from Timestamp.proto: | ||||
|                 // "Negative second values with fractions must still have | ||||
|                 // non -negative nanos values that count forward in time." | ||||
|                 seconds--; | ||||
|                 nanos += 1000000000; | ||||
|             } | ||||
|             WriteSecondsNanos(seconds, nanos, dest); | ||||
|         } | ||||
| 
 | ||||
|         static TimeSpan FromDurationSeconds(long seconds, int nanos) | ||||
|         { | ||||
| 
 | ||||
|             long ticks = checked((seconds * TimeSpan.TicksPerSecond) | ||||
|                 + (nanos * TimeSpan.TicksPerMillisecond) / 1000000); | ||||
|             return TimeSpan.FromTicks(ticks); | ||||
|         } | ||||
| 
 | ||||
|         static long ToDurationSeconds(TimeSpan value, out int nanos) | ||||
|         { | ||||
|             nanos = (int)(((value.Ticks % TimeSpan.TicksPerSecond) * 1000000) | ||||
|                 / TimeSpan.TicksPerMillisecond); | ||||
|             return value.Ticks / TimeSpan.TicksPerSecond; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Parses a DateTime from a protobuf stream | ||||
|         /// </summary> | ||||
|         public static DateTime ReadDateTime(ProtoReader source) | ||||
|         { | ||||
|             long ticks = ReadTimeSpanTicks(source, out DateTimeKind kind); | ||||
|             if (ticks == long.MinValue) return DateTime.MinValue; | ||||
|             if (ticks == long.MaxValue) return DateTime.MaxValue; | ||||
|             return EpochOrigin[(int)kind].AddTicks(ticks); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a DateTime to a protobuf stream, excluding the <c>Kind</c> | ||||
|         /// </summary> | ||||
|         public static void WriteDateTime(DateTime value, ProtoWriter dest) | ||||
|         { | ||||
|             WriteDateTimeImpl(value, dest, false); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a DateTime to a protobuf stream, including the <c>Kind</c> | ||||
|         /// </summary> | ||||
|         public static void WriteDateTimeWithKind(DateTime value, ProtoWriter dest) | ||||
|         { | ||||
|             WriteDateTimeImpl(value, dest, true); | ||||
|         } | ||||
| 
 | ||||
|         private static void WriteDateTimeImpl(DateTime value, ProtoWriter dest, bool includeKind) | ||||
|         { | ||||
|             if (dest == null) throw new ArgumentNullException(nameof(dest)); | ||||
|             TimeSpan delta; | ||||
|             switch (dest.WireType) | ||||
|             { | ||||
|                 case WireType.StartGroup: | ||||
|                 case WireType.String: | ||||
|                     if (value == DateTime.MaxValue) | ||||
|                     { | ||||
|                         delta = TimeSpan.MaxValue; | ||||
|                         includeKind = false; | ||||
|                     } | ||||
|                     else if (value == DateTime.MinValue) | ||||
|                     { | ||||
|                         delta = TimeSpan.MinValue; | ||||
|                         includeKind = false; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         delta = value - EpochOrigin[0]; | ||||
|                     } | ||||
|                     break; | ||||
|                 default: | ||||
|                     delta = value - EpochOrigin[0]; | ||||
|                     break; | ||||
|             } | ||||
|             WriteTimeSpanImpl(delta, dest, includeKind ? value.Kind : DateTimeKind.Unspecified); | ||||
|         } | ||||
| 
 | ||||
|         private static long ReadTimeSpanTicks(ProtoReader source, out DateTimeKind kind) | ||||
|         { | ||||
|             kind = DateTimeKind.Unspecified; | ||||
|             switch (source.WireType) | ||||
|             { | ||||
|                 case WireType.String: | ||||
|                 case WireType.StartGroup: | ||||
|                     SubItemToken token = ProtoReader.StartSubItem(source); | ||||
|                     int fieldNumber; | ||||
|                     TimeSpanScale scale = TimeSpanScale.Days; | ||||
|                     long value = 0; | ||||
|                     while ((fieldNumber = source.ReadFieldHeader()) > 0) | ||||
|                     { | ||||
|                         switch (fieldNumber) | ||||
|                         { | ||||
|                             case FieldTimeSpanScale: | ||||
|                                 scale = (TimeSpanScale)source.ReadInt32(); | ||||
|                                 break; | ||||
|                             case FieldTimeSpanValue: | ||||
|                                 source.Assert(WireType.SignedVariant); | ||||
|                                 value = source.ReadInt64(); | ||||
|                                 break; | ||||
|                             case FieldTimeSpanKind: | ||||
|                                 kind = (DateTimeKind)source.ReadInt32(); | ||||
|                                 switch (kind) | ||||
|                                 { | ||||
|                                     case DateTimeKind.Unspecified: | ||||
|                                     case DateTimeKind.Utc: | ||||
|                                     case DateTimeKind.Local: | ||||
|                                         break; // fine | ||||
|                                     default: | ||||
|                                         throw new ProtoException("Invalid date/time kind: " + kind.ToString()); | ||||
|                                 } | ||||
|                                 break; | ||||
|                             default: | ||||
|                                 source.SkipField(); | ||||
|                                 break; | ||||
|                         } | ||||
|                     } | ||||
|                     ProtoReader.EndSubItem(token, source); | ||||
|                     switch (scale) | ||||
|                     { | ||||
|                         case TimeSpanScale.Days: | ||||
|                             return value * TimeSpan.TicksPerDay; | ||||
|                         case TimeSpanScale.Hours: | ||||
|                             return value * TimeSpan.TicksPerHour; | ||||
|                         case TimeSpanScale.Minutes: | ||||
|                             return value * TimeSpan.TicksPerMinute; | ||||
|                         case TimeSpanScale.Seconds: | ||||
|                             return value * TimeSpan.TicksPerSecond; | ||||
|                         case TimeSpanScale.Milliseconds: | ||||
|                             return value * TimeSpan.TicksPerMillisecond; | ||||
|                         case TimeSpanScale.Ticks: | ||||
|                             return value; | ||||
|                         case TimeSpanScale.MinMax: | ||||
|                             switch (value) | ||||
|                             { | ||||
|                                 case 1: return long.MaxValue; | ||||
|                                 case -1: return long.MinValue; | ||||
|                                 default: throw new ProtoException("Unknown min/max value: " + value.ToString()); | ||||
|                             } | ||||
|                         default: | ||||
|                             throw new ProtoException("Unknown timescale: " + scale.ToString()); | ||||
|                     } | ||||
|                 case WireType.Fixed64: | ||||
|                     return source.ReadInt64(); | ||||
|                 default: | ||||
|                     throw new ProtoException("Unexpected wire-type: " + source.WireType.ToString()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const int FieldDecimalLow = 0x01, FieldDecimalHigh = 0x02, FieldDecimalSignScale = 0x03; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Parses a decimal from a protobuf stream | ||||
|         /// </summary> | ||||
|         public static decimal ReadDecimal(ProtoReader reader) | ||||
|         { | ||||
|             ulong low = 0; | ||||
|             uint high = 0; | ||||
|             uint signScale = 0; | ||||
| 
 | ||||
|             int fieldNumber; | ||||
|             SubItemToken token = ProtoReader.StartSubItem(reader); | ||||
|             while ((fieldNumber = reader.ReadFieldHeader()) > 0) | ||||
|             { | ||||
|                 switch (fieldNumber) | ||||
|                 { | ||||
|                     case FieldDecimalLow: low = reader.ReadUInt64(); break; | ||||
|                     case FieldDecimalHigh: high = reader.ReadUInt32(); break; | ||||
|                     case FieldDecimalSignScale: signScale = reader.ReadUInt32(); break; | ||||
|                     default: reader.SkipField(); break; | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             ProtoReader.EndSubItem(token, reader); | ||||
| 
 | ||||
|             int lo = (int)(low & 0xFFFFFFFFL), | ||||
|                 mid = (int)((low >> 32) & 0xFFFFFFFFL), | ||||
|                 hi = (int)high; | ||||
|             bool isNeg = (signScale & 0x0001) == 0x0001; | ||||
|             byte scale = (byte)((signScale & 0x01FE) >> 1); | ||||
|             return new decimal(lo, mid, hi, isNeg, scale); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes a decimal to a protobuf stream | ||||
|         /// </summary> | ||||
|         public static void WriteDecimal(decimal value, ProtoWriter writer) | ||||
|         { | ||||
|             int[] bits = decimal.GetBits(value); | ||||
|             ulong a = ((ulong)bits[1]) << 32, b = ((ulong)bits[0]) & 0xFFFFFFFFL; | ||||
|             ulong low = a | b; | ||||
|             uint high = (uint)bits[2]; | ||||
|             uint signScale = (uint)(((bits[3] >> 15) & 0x01FE) | ((bits[3] >> 31) & 0x0001)); | ||||
| 
 | ||||
|             SubItemToken token = ProtoWriter.StartSubItem(null, writer); | ||||
|             if (low != 0) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(FieldDecimalLow, WireType.Variant, writer); | ||||
|                 ProtoWriter.WriteUInt64(low, writer); | ||||
|             } | ||||
|             if (high != 0) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(FieldDecimalHigh, WireType.Variant, writer); | ||||
|                 ProtoWriter.WriteUInt32(high, writer); | ||||
|             } | ||||
|             if (signScale != 0) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(FieldDecimalSignScale, WireType.Variant, writer); | ||||
|                 ProtoWriter.WriteUInt32(signScale, writer); | ||||
|             } | ||||
|             ProtoWriter.EndSubItem(token, writer); | ||||
|         } | ||||
| 
 | ||||
|         const int FieldGuidLow = 1, FieldGuidHigh = 2; | ||||
|         /// <summary> | ||||
|         /// Writes a Guid to a protobuf stream | ||||
|         /// </summary>         | ||||
|         public static void WriteGuid(Guid value, ProtoWriter dest) | ||||
|         { | ||||
|             byte[] blob = value.ToByteArray(); | ||||
| 
 | ||||
|             SubItemToken token = ProtoWriter.StartSubItem(null, dest); | ||||
|             if (value != Guid.Empty) | ||||
|             { | ||||
|                 ProtoWriter.WriteFieldHeader(FieldGuidLow, WireType.Fixed64, dest); | ||||
|                 ProtoWriter.WriteBytes(blob, 0, 8, dest); | ||||
|                 ProtoWriter.WriteFieldHeader(FieldGuidHigh, WireType.Fixed64, dest); | ||||
|                 ProtoWriter.WriteBytes(blob, 8, 8, dest); | ||||
|             } | ||||
|             ProtoWriter.EndSubItem(token, dest); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Parses a Guid from a protobuf stream | ||||
|         /// </summary> | ||||
|         public static Guid ReadGuid(ProtoReader source) | ||||
|         { | ||||
|             ulong low = 0, high = 0; | ||||
|             int fieldNumber; | ||||
|             SubItemToken token = ProtoReader.StartSubItem(source); | ||||
|             while ((fieldNumber = source.ReadFieldHeader()) > 0) | ||||
|             { | ||||
|                 switch (fieldNumber) | ||||
|                 { | ||||
|                     case FieldGuidLow: low = source.ReadUInt64(); break; | ||||
|                     case FieldGuidHigh: high = source.ReadUInt64(); break; | ||||
|                     default: source.SkipField(); break; | ||||
|                 } | ||||
|             } | ||||
|             ProtoReader.EndSubItem(token, source); | ||||
|             if (low == 0 && high == 0) return Guid.Empty; | ||||
|             uint a = (uint)(low >> 32), b = (uint)low, c = (uint)(high >> 32), d = (uint)high; | ||||
|             return new Guid((int)b, (short)a, (short)(a >> 16), | ||||
|                 (byte)d, (byte)(d >> 8), (byte)(d >> 16), (byte)(d >> 24), | ||||
|                 (byte)c, (byte)(c >> 8), (byte)(c >> 16), (byte)(c >> 24)); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         private const int | ||||
|             FieldExistingObjectKey = 1, | ||||
|             FieldNewObjectKey = 2, | ||||
|             FieldExistingTypeKey = 3, | ||||
|             FieldNewTypeKey = 4, | ||||
|             FieldTypeName = 8, | ||||
|             FieldObject = 10; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Optional behaviours that introduce .NET-specific functionality | ||||
|         /// </summary> | ||||
|         [Flags] | ||||
|         public enum NetObjectOptions : byte | ||||
|         { | ||||
|             /// <summary> | ||||
|             /// No special behaviour | ||||
|             /// </summary> | ||||
|             None = 0, | ||||
|             /// <summary> | ||||
|             /// Enables full object-tracking/full-graph support. | ||||
|             /// </summary> | ||||
|             AsReference = 1, | ||||
|             /// <summary> | ||||
|             /// Embeds the type information into the stream, allowing usage with types not known in advance. | ||||
|             /// </summary> | ||||
|             DynamicType = 2, | ||||
|             /// <summary> | ||||
|             /// If false, the constructor for the type is bypassed during deserialization, meaning any field initializers | ||||
|             /// or other initialization code is skipped. | ||||
|             /// </summary> | ||||
|             UseConstructor = 4, | ||||
|             /// <summary> | ||||
|             /// Should the object index be reserved, rather than creating an object promptly | ||||
|             /// </summary> | ||||
|             LateSet = 8 | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. | ||||
|         /// </summary> | ||||
|         public static object ReadNetObject(object value, ProtoReader source, int key, Type type, NetObjectOptions options) | ||||
|         { | ||||
|             SubItemToken token = ProtoReader.StartSubItem(source); | ||||
|             int fieldNumber; | ||||
|             int newObjectKey = -1, newTypeKey = -1, tmp; | ||||
|             while ((fieldNumber = source.ReadFieldHeader()) > 0) | ||||
|             { | ||||
|                 switch (fieldNumber) | ||||
|                 { | ||||
|                     case FieldExistingObjectKey: | ||||
|                         tmp = source.ReadInt32(); | ||||
|                         value = source.NetCache.GetKeyedObject(tmp); | ||||
|                         break; | ||||
|                     case FieldNewObjectKey: | ||||
|                         newObjectKey = source.ReadInt32(); | ||||
|                         break; | ||||
|                     case FieldExistingTypeKey: | ||||
|                         tmp = source.ReadInt32(); | ||||
|                         type = (Type)source.NetCache.GetKeyedObject(tmp); | ||||
|                         key = source.GetTypeKey(ref type); | ||||
|                         break; | ||||
|                     case FieldNewTypeKey: | ||||
|                         newTypeKey = source.ReadInt32(); | ||||
|                         break; | ||||
|                     case FieldTypeName: | ||||
|                         string typeName = source.ReadString(); | ||||
|                         type = source.DeserializeType(typeName); | ||||
|                         if (type == null) | ||||
|                         { | ||||
|                             throw new ProtoException("Unable to resolve type: " + typeName + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)"); | ||||
|                         } | ||||
|                         if (type == typeof(string)) | ||||
|                         { | ||||
|                             key = -1; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             key = source.GetTypeKey(ref type); | ||||
|                             if (key < 0) | ||||
|                                 throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name); | ||||
|                         } | ||||
|                         break; | ||||
|                     case FieldObject: | ||||
|                         bool isString = type == typeof(string); | ||||
|                         bool wasNull = value == null; | ||||
|                         bool lateSet = wasNull && (isString || ((options & NetObjectOptions.LateSet) != 0)); | ||||
| 
 | ||||
|                         if (newObjectKey >= 0 && !lateSet) | ||||
|                         { | ||||
|                             if (value == null) | ||||
|                             { | ||||
|                                 source.TrapNextObject(newObjectKey); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 source.NetCache.SetKeyedObject(newObjectKey, value); | ||||
|                             } | ||||
|                             if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type); | ||||
|                         } | ||||
|                         object oldValue = value; | ||||
|                         if (isString) | ||||
|                         { | ||||
|                             value = source.ReadString(); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             value = ProtoReader.ReadTypedObject(oldValue, key, source, type); | ||||
|                         } | ||||
| 
 | ||||
|                         if (newObjectKey >= 0) | ||||
|                         { | ||||
|                             if (wasNull && !lateSet) | ||||
|                             { // this both ensures (via exception) that it *was* set, and makes sure we don't shout | ||||
|                                 // about changed references | ||||
|                                 oldValue = source.NetCache.GetKeyedObject(newObjectKey); | ||||
|                             } | ||||
|                             if (lateSet) | ||||
|                             { | ||||
|                                 source.NetCache.SetKeyedObject(newObjectKey, value); | ||||
|                                 if (newTypeKey >= 0) source.NetCache.SetKeyedObject(newTypeKey, type); | ||||
|                             } | ||||
|                         } | ||||
|                         if (newObjectKey >= 0 && !lateSet && !ReferenceEquals(oldValue, value)) | ||||
|                         { | ||||
|                             throw new ProtoException("A reference-tracked object changed reference during deserialization"); | ||||
|                         } | ||||
|                         if (newObjectKey < 0 && newTypeKey >= 0) | ||||
|                         {  // have a new type, but not a new object | ||||
|                             source.NetCache.SetKeyedObject(newTypeKey, type); | ||||
|                         } | ||||
|                         break; | ||||
|                     default: | ||||
|                         source.SkipField(); | ||||
|                         break; | ||||
|                 } | ||||
|             } | ||||
|             if (newObjectKey >= 0 && (options & NetObjectOptions.AsReference) == 0) | ||||
|             { | ||||
|                 throw new ProtoException("Object key in input stream, but reference-tracking was not expected"); | ||||
|             } | ||||
|             ProtoReader.EndSubItem(token, source); | ||||
| 
 | ||||
|             return value; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Writes an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. | ||||
|         /// </summary> | ||||
|         public static void WriteNetObject(object value, ProtoWriter dest, int key, NetObjectOptions options) | ||||
|         { | ||||
|             if (dest == null) throw new ArgumentNullException("dest"); | ||||
|             bool dynamicType = (options & NetObjectOptions.DynamicType) != 0, | ||||
|                  asReference = (options & NetObjectOptions.AsReference) != 0; | ||||
|             WireType wireType = dest.WireType; | ||||
|             SubItemToken token = ProtoWriter.StartSubItem(null, dest); | ||||
|             bool writeObject = true; | ||||
|             if (asReference) | ||||
|             { | ||||
|                 int objectKey = dest.NetCache.AddObjectKey(value, out bool existing); | ||||
|                 ProtoWriter.WriteFieldHeader(existing ? FieldExistingObjectKey : FieldNewObjectKey, WireType.Variant, dest); | ||||
|                 ProtoWriter.WriteInt32(objectKey, dest); | ||||
|                 if (existing) | ||||
|                 { | ||||
|                     writeObject = false; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (writeObject) | ||||
|             { | ||||
|                 if (dynamicType) | ||||
|                 { | ||||
|                     Type type = value.GetType(); | ||||
| 
 | ||||
|                     if (!(value is string)) | ||||
|                     { | ||||
|                         key = dest.GetTypeKey(ref type); | ||||
|                         if (key < 0) throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name); | ||||
|                     } | ||||
|                     int typeKey = dest.NetCache.AddObjectKey(type, out bool existing); | ||||
|                     ProtoWriter.WriteFieldHeader(existing ? FieldExistingTypeKey : FieldNewTypeKey, WireType.Variant, dest); | ||||
|                     ProtoWriter.WriteInt32(typeKey, dest); | ||||
|                     if (!existing) | ||||
|                     { | ||||
|                         ProtoWriter.WriteFieldHeader(FieldTypeName, WireType.String, dest); | ||||
|                         ProtoWriter.WriteString(dest.SerializeType(type), dest); | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
|                 ProtoWriter.WriteFieldHeader(FieldObject, wireType, dest); | ||||
|                 if (value is string) | ||||
|                 { | ||||
|                     ProtoWriter.WriteString((string)value, dest); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ProtoWriter.WriteObject(value, key, dest); | ||||
|                 } | ||||
|             } | ||||
|             ProtoWriter.EndSubItem(token, dest); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 5072fbed211eb9f43a3cd2805dd75ef7 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,78 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Provides a simple buffer-based implementation of an <see cref="IExtension">extension</see> object. | ||||
|     /// </summary> | ||||
|     public sealed class BufferExtension : IExtension, IExtensionResettable | ||||
|     { | ||||
|         private byte[] buffer; | ||||
| 
 | ||||
|         void IExtensionResettable.Reset() | ||||
|         { | ||||
|             buffer = null; | ||||
|         } | ||||
| 
 | ||||
|         int IExtension.GetLength() | ||||
|         { | ||||
|             return buffer == null ? 0 : buffer.Length; | ||||
|         } | ||||
| 
 | ||||
|         Stream IExtension.BeginAppend() | ||||
|         { | ||||
|             return new MemoryStream(); | ||||
|         } | ||||
| 
 | ||||
|         void IExtension.EndAppend(Stream stream, bool commit) | ||||
|         { | ||||
|             using (stream) | ||||
|             { | ||||
|                 int len; | ||||
|                 if (commit && (len = (int)stream.Length) > 0) | ||||
|                 { | ||||
|                     MemoryStream ms = (MemoryStream)stream; | ||||
| 
 | ||||
|                     if (buffer == null) | ||||
|                     {   // allocate new buffer | ||||
|                         buffer = ms.ToArray(); | ||||
|                     } | ||||
|                     else | ||||
|                     {   // resize and copy the data | ||||
|                         // note: Array.Resize not available on CF | ||||
|                         int offset = buffer.Length; | ||||
|                         byte[] tmp = new byte[offset + len]; | ||||
|                         Buffer.BlockCopy(buffer, 0, tmp, 0, offset); | ||||
| 
 | ||||
| #if PORTABLE // no GetBuffer() - fine, we'll use Read instead | ||||
|                         int bytesRead; | ||||
|                         long oldPos = ms.Position; | ||||
|                         ms.Position = 0; | ||||
|                         while (len > 0 && (bytesRead = ms.Read(tmp, offset, len)) > 0) | ||||
|                         { | ||||
|                             len -= bytesRead; | ||||
|                             offset += bytesRead; | ||||
|                         } | ||||
|                         if(len != 0) throw new EndOfStreamException(); | ||||
|                         ms.Position = oldPos; | ||||
| #else | ||||
|                         Buffer.BlockCopy(Helpers.GetBuffer(ms), 0, tmp, offset, len); | ||||
| #endif | ||||
|                         buffer = tmp; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Stream IExtension.BeginQuery() | ||||
|         { | ||||
|             return buffer == null ? Stream.Null : new MemoryStream(buffer); | ||||
|         } | ||||
| 
 | ||||
|         void IExtension.EndQuery(Stream stream) | ||||
|         { | ||||
|             using (stream) { } // just clean up | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a9cf66041a027e94892d5014c2b905b3 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,149 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     internal sealed class BufferPool | ||||
|     { | ||||
|         internal static void Flush() | ||||
|         { | ||||
|             lock (Pool) | ||||
|             { | ||||
|                 for (var i = 0; i < Pool.Length; i++) | ||||
|                     Pool[i] = null; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private BufferPool() { } | ||||
|         private const int POOL_SIZE = 20; | ||||
|         internal const int BUFFER_LENGTH = 1024; | ||||
|         private static readonly CachedBuffer[] Pool = new CachedBuffer[POOL_SIZE]; | ||||
| 
 | ||||
|         internal static byte[] GetBuffer() => GetBuffer(BUFFER_LENGTH); | ||||
| 
 | ||||
|         internal static byte[] GetBuffer(int minSize) | ||||
|         { | ||||
|             byte[] cachedBuff = GetCachedBuffer(minSize); | ||||
|             return cachedBuff ?? new byte[minSize]; | ||||
|         } | ||||
| 
 | ||||
|         internal static byte[] GetCachedBuffer(int minSize) | ||||
|         { | ||||
|             lock (Pool) | ||||
|             { | ||||
|                 var bestIndex = -1; | ||||
|                 byte[] bestMatch = null; | ||||
|                 for (var i = 0; i < Pool.Length; i++) | ||||
|                 { | ||||
|                     var buffer = Pool[i]; | ||||
|                     if (buffer == null || buffer.Size < minSize) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
|                     if (bestMatch != null && bestMatch.Length < buffer.Size) | ||||
|                     { | ||||
|                         continue; | ||||
|                     } | ||||
| 
 | ||||
|                     var tmp = buffer.Buffer; | ||||
|                     if (tmp == null) | ||||
|                     { | ||||
|                         Pool[i] = null; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         bestMatch = tmp; | ||||
|                         bestIndex = i; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (bestIndex >= 0) | ||||
|                 { | ||||
|                     Pool[bestIndex] = null; | ||||
|                 } | ||||
| 
 | ||||
|                 return bestMatch; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <remarks> | ||||
|         /// https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element | ||||
|         /// </remarks> | ||||
|         private const int MaxByteArraySize = int.MaxValue - 56; | ||||
| 
 | ||||
|         internal static void ResizeAndFlushLeft(ref byte[] buffer, int toFitAtLeastBytes, int copyFromIndex, int copyBytes) | ||||
|         { | ||||
|             Helpers.DebugAssert(buffer != null); | ||||
|             Helpers.DebugAssert(toFitAtLeastBytes > buffer.Length); | ||||
|             Helpers.DebugAssert(copyFromIndex >= 0); | ||||
|             Helpers.DebugAssert(copyBytes >= 0); | ||||
| 
 | ||||
|             int newLength = buffer.Length * 2; | ||||
|             if (newLength < 0) | ||||
|             { | ||||
|                 newLength = MaxByteArraySize; | ||||
|             } | ||||
| 
 | ||||
|             if (newLength < toFitAtLeastBytes) newLength = toFitAtLeastBytes; | ||||
| 
 | ||||
|             if (copyBytes == 0) | ||||
|             { | ||||
|                 ReleaseBufferToPool(ref buffer); | ||||
|             } | ||||
| 
 | ||||
|             var newBuffer = GetCachedBuffer(toFitAtLeastBytes) ?? new byte[newLength]; | ||||
| 
 | ||||
|             if (copyBytes > 0) | ||||
|             { | ||||
|                 Buffer.BlockCopy(buffer, copyFromIndex, newBuffer, 0, copyBytes); | ||||
|                 ReleaseBufferToPool(ref buffer); | ||||
|             } | ||||
| 
 | ||||
|             buffer = newBuffer; | ||||
|         } | ||||
| 
 | ||||
|         internal static void ReleaseBufferToPool(ref byte[] buffer) | ||||
|         { | ||||
|             if (buffer == null) return; | ||||
| 
 | ||||
|             lock (Pool) | ||||
|             { | ||||
|                 var minIndex = 0; | ||||
|                 var minSize = int.MaxValue; | ||||
|                 for (var i = 0; i < Pool.Length; i++) | ||||
|                 { | ||||
|                     var tmp = Pool[i]; | ||||
|                     if (tmp == null || !tmp.IsAlive) | ||||
|                     { | ||||
|                         minIndex = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                     if (tmp.Size < minSize) | ||||
|                     { | ||||
|                         minIndex = i; | ||||
|                         minSize = tmp.Size; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 Pool[minIndex] = new CachedBuffer(buffer); | ||||
|             } | ||||
| 
 | ||||
|             buffer = null; | ||||
|         } | ||||
| 
 | ||||
|         private class CachedBuffer | ||||
|         { | ||||
|             private readonly WeakReference _reference; | ||||
| 
 | ||||
|             public int Size { get; } | ||||
| 
 | ||||
|             public bool IsAlive => _reference.IsAlive; | ||||
|             public byte[] Buffer => (byte[])_reference.Target; | ||||
| 
 | ||||
|             public CachedBuffer(byte[] buffer) | ||||
|             { | ||||
|                 Size = buffer.Length; | ||||
|                 _reference = new WeakReference(buffer); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 423b228ed060b91458bc6d4e6aa0f570 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,33 @@ | |||
| using System; | ||||
| using System.ComponentModel; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked before serialization.</summary> | ||||
|     [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | ||||
| #if !CF && !PORTABLE && !COREFX && !PROFILE259 | ||||
|     [ImmutableObject(true)] | ||||
| #endif | ||||
|     public sealed class ProtoBeforeSerializationAttribute : Attribute { } | ||||
| 
 | ||||
|     /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked after serialization.</summary> | ||||
|     [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | ||||
| #if !CF && !PORTABLE && !COREFX && !PROFILE259 | ||||
|     [ImmutableObject(true)] | ||||
| #endif | ||||
|     public sealed class ProtoAfterSerializationAttribute : Attribute { } | ||||
| 
 | ||||
|     /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked before deserialization.</summary> | ||||
|     [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | ||||
| #if !CF && !PORTABLE && !COREFX && !PROFILE259 | ||||
|     [ImmutableObject(true)] | ||||
| #endif | ||||
|     public sealed class ProtoBeforeDeserializationAttribute : Attribute { } | ||||
| 
 | ||||
|     /// <summary>Specifies a method on the root-contract in an hierarchy to be invoked after deserialization.</summary> | ||||
|     [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] | ||||
| #if !CF && !PORTABLE && !COREFX && !PROFILE259 | ||||
|     [ImmutableObject(true)] | ||||
| #endif | ||||
|     public sealed class ProtoAfterDeserializationAttribute : Attribute { } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 53de2cb3784c9dd43aa6f30d7df072a4 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 2cdd9eb2afa3ed24480a6035f507aad4 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a58d20a1d8c7730499ef29a11532d07e | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,7 @@ | |||
| #if FEAT_COMPILER | ||||
| namespace ProtoBuf.Compiler | ||||
| { | ||||
|     internal delegate void ProtoSerializer(object value, ProtoWriter dest); | ||||
|     internal delegate object ProtoDeserializer(object value, ProtoReader source); | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 3b923d7ab8e95f740b059ca797596261 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,58 @@ | |||
| #if FEAT_COMPILER | ||||
| using System; | ||||
| using System.Reflection.Emit; | ||||
| 
 | ||||
| namespace ProtoBuf.Compiler | ||||
| { | ||||
|     internal sealed class Local : IDisposable | ||||
|     { | ||||
|         // public static readonly Local InputValue = new Local(null, null); | ||||
|         private LocalBuilder value; | ||||
|         private readonly Type type; | ||||
|         private CompilerContext ctx; | ||||
| 
 | ||||
|         private Local(LocalBuilder value, Type type) | ||||
|         { | ||||
|             this.value = value; | ||||
|             this.type = type; | ||||
|         } | ||||
| 
 | ||||
|         internal Local(CompilerContext ctx, Type type) | ||||
|         { | ||||
|             this.ctx = ctx; | ||||
|             if (ctx != null) { value = ctx.GetFromPool(type); } | ||||
|             this.type = type; | ||||
|         } | ||||
| 
 | ||||
|         internal LocalBuilder Value => value ?? throw new ObjectDisposedException(GetType().Name); | ||||
| 
 | ||||
|         public Type Type => type; | ||||
| 
 | ||||
|         public Local AsCopy() | ||||
|         { | ||||
|             if (ctx == null) return this; // can re-use if context-free | ||||
|             return new Local(value, this.type); | ||||
|         } | ||||
|          | ||||
|         public void Dispose() | ||||
|         { | ||||
|             if (ctx != null) | ||||
|             { | ||||
|                 // only *actually* dispose if this is context-bound; note that non-bound | ||||
|                 // objects are cheekily re-used, and *must* be left intact agter a "using" etc | ||||
|                 ctx.ReleaseToPool(value); | ||||
|                 value = null;  | ||||
|                 ctx = null; | ||||
|             }             | ||||
|         } | ||||
| 
 | ||||
|         internal bool IsSame(Local other) | ||||
|         { | ||||
|             if((object)this == (object)other) return true; | ||||
| 
 | ||||
|             object ourVal = value; // use prop to ensure obj-disposed etc | ||||
|             return other != null && ourVal == (object)(other.value);  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 07d12d9a9b7d45b498e28b7c39bdca01 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,49 @@ | |||
|  | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Sub-format to use when serializing/deserializing data | ||||
|     /// </summary> | ||||
|     public enum DataFormat | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Uses the default encoding for the data-type. | ||||
|         /// </summary> | ||||
|         Default, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// When applied to signed integer-based data (including Decimal), this | ||||
|         /// indicates that zigzag variant encoding will be used. This means that values | ||||
|         /// with small magnitude (regardless of sign) take a small amount | ||||
|         /// of space to encode. | ||||
|         /// </summary> | ||||
|         ZigZag, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// When applied to signed integer-based data (including Decimal), this | ||||
|         /// indicates that two's-complement variant encoding will be used. | ||||
|         /// This means that any -ve number will take 10 bytes (even for 32-bit), | ||||
|         /// so should only be used for compatibility. | ||||
|         /// </summary> | ||||
|         TwosComplement, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// When applied to signed integer-based data (including Decimal), this | ||||
|         /// indicates that a fixed amount of space will be used. | ||||
|         /// </summary> | ||||
|         FixedSize, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// When applied to a sub-message, indicates that the value should be treated | ||||
|         /// as group-delimited. | ||||
|         /// </summary> | ||||
|         Group, | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// When applied to members of types such as DateTime or TimeSpan, specifies | ||||
|         /// that the "well known" standardized representation should be use; DateTime uses Timestamp, | ||||
|         ///  | ||||
|         /// </summary> | ||||
|         WellKnown | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 875f2f7de4b03ff409de70d226359e8f | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,176 @@ | |||
| #if PLAT_BINARYFORMATTER | ||||
| using System; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Runtime.Serialization; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnionObject : ISerializable | ||||
|     { | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (Discriminator != default) info.AddValue("d", Discriminator); | ||||
|             if (Object is object) info.AddValue("o", Object); | ||||
|         } | ||||
|         private DiscriminatedUnionObject(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": Discriminator = (int)field.Value; break; | ||||
|                     case "o": Object = field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion128Object : ISerializable | ||||
|     { | ||||
|         [FieldOffset(8)] private readonly long _lo; | ||||
|         [FieldOffset(16)] private readonly long _hi; | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (_lo != default) info.AddValue("l", _lo); | ||||
|             if (_hi != default) info.AddValue("h", _hi); | ||||
|             if (Object != null) info.AddValue("o", Object); | ||||
|         } | ||||
|         private DiscriminatedUnion128Object(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "l": _lo = (long)field.Value; break; | ||||
|                     case "h": _hi = (long)field.Value; break; | ||||
|                     case "o": Object = field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion128 : ISerializable | ||||
|     { | ||||
|         [FieldOffset(8)] private readonly long _lo; | ||||
|         [FieldOffset(16)] private readonly long _hi; | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (_lo != default) info.AddValue("l", _lo); | ||||
|             if (_hi != default) info.AddValue("h", _hi); | ||||
|         } | ||||
|         private DiscriminatedUnion128(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "l": _lo = (long)field.Value; break; | ||||
|                     case "h": _hi = (long)field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion64 : ISerializable | ||||
|     { | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (Int64 != default) info.AddValue("i", Int64); | ||||
|         } | ||||
|         private DiscriminatedUnion64(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "i": Int64 = (long)field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion64Object : ISerializable | ||||
|     { | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (Int64 != default) info.AddValue("i", Int64); | ||||
|             if (Object is object) info.AddValue("o", Object); | ||||
|         } | ||||
|         private DiscriminatedUnion64Object(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "i": Int64 = (long)field.Value; break; | ||||
|                     case "o": Object = field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion32 : ISerializable | ||||
|     { | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (Int32 != default) info.AddValue("i", Int32); | ||||
|         } | ||||
|         private DiscriminatedUnion32(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "i": Int32 = (int)field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [Serializable] | ||||
|     public readonly partial struct DiscriminatedUnion32Object : ISerializable | ||||
|     { | ||||
|         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             if (_discriminator != default) info.AddValue("d", _discriminator); | ||||
|             if (Int32 != default) info.AddValue("i", Int32); | ||||
|             if (Object is object) info.AddValue("o", Object); | ||||
|         } | ||||
|         private DiscriminatedUnion32Object(SerializationInfo info, StreamingContext context) | ||||
|         { | ||||
|             this = default; | ||||
|             foreach (var field in info) | ||||
|             { | ||||
|                 switch (field.Name) | ||||
|                 { | ||||
|                     case "d": _discriminator = (int)field.Value; break; | ||||
|                     case "i": Int32 = (int)field.Value; break; | ||||
|                     case "o": Object = field.Value; break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 7a3aeec9c8a4c734e9ad022627502d1d | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,416 @@ | |||
| using System; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     public readonly partial struct DiscriminatedUnionObject | ||||
|     { | ||||
| 
 | ||||
|         /// <summary>The value typed as Object</summary> | ||||
|         public readonly object Object; | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => Discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnionObject(int discriminator, object value) | ||||
|         { | ||||
|             Discriminator = discriminator; | ||||
|             Object = value; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnionObject value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator { get; } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion64 | ||||
|     { | ||||
| #if !FEAT_SAFE | ||||
| 		unsafe static DiscriminatedUnion64() | ||||
|         { | ||||
|             if (sizeof(DateTime) > 8) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64)); | ||||
|             if (sizeof(TimeSpan) > 8) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64)); | ||||
|         } | ||||
| #endif | ||||
| 		[FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64 | ||||
| 
 | ||||
|         /// <summary>The value typed as Int64</summary> | ||||
|         [FieldOffset(8)] public readonly long Int64; | ||||
|         /// <summary>The value typed as UInt64</summary> | ||||
|         [FieldOffset(8)] public readonly ulong UInt64; | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(8)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(8)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(8)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(8)] public readonly float Single; | ||||
|         /// <summary>The value typed as Double</summary> | ||||
|         [FieldOffset(8)] public readonly double Double; | ||||
|         /// <summary>The value typed as DateTime</summary> | ||||
|         [FieldOffset(8)] public readonly DateTime DateTime; | ||||
|         /// <summary>The value typed as TimeSpan</summary> | ||||
|         [FieldOffset(8)] public readonly TimeSpan TimeSpan; | ||||
| 
 | ||||
|         private DiscriminatedUnion64(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, long value) : this(discriminator) { Int64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, ulong value) : this(discriminator) { UInt64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, double value) : this(discriminator) { Double = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion64 value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion128Object | ||||
|     { | ||||
| #if !FEAT_SAFE | ||||
| 		unsafe static DiscriminatedUnion128Object() | ||||
|         { | ||||
|             if (sizeof(DateTime) > 16) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object)); | ||||
|             if (sizeof(TimeSpan) > 16) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object)); | ||||
|             if (sizeof(Guid) > 16) throw new InvalidOperationException(nameof(Guid) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128Object)); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
| 		[FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64 | ||||
| 
 | ||||
|         /// <summary>The value typed as Int64</summary> | ||||
|         [FieldOffset(8)] public readonly long Int64; | ||||
|         /// <summary>The value typed as UInt64</summary> | ||||
|         [FieldOffset(8)] public readonly ulong UInt64; | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(8)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(8)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(8)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(8)] public readonly float Single; | ||||
|         /// <summary>The value typed as Double</summary> | ||||
|         [FieldOffset(8)] public readonly double Double; | ||||
|         /// <summary>The value typed as DateTime</summary> | ||||
|         [FieldOffset(8)] public readonly DateTime DateTime; | ||||
|         /// <summary>The value typed as TimeSpan</summary> | ||||
|         [FieldOffset(8)] public readonly TimeSpan TimeSpan; | ||||
|         /// <summary>The value typed as Guid</summary> | ||||
|         [FieldOffset(8)] public readonly Guid Guid; | ||||
|         /// <summary>The value typed as Object</summary> | ||||
|         [FieldOffset(24)] public readonly object Object; | ||||
| 
 | ||||
|         private DiscriminatedUnion128Object(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, long value) : this(discriminator) { Int64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, ulong value) : this(discriminator) { UInt64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, double value) : this(discriminator) { Double = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128Object(int discriminator, Guid? value) : this(value.HasValue ? discriminator : 0) { Guid = value.GetValueOrDefault(); } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion128Object value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion128 | ||||
|     { | ||||
| #if !FEAT_SAFE | ||||
|         unsafe static DiscriminatedUnion128() | ||||
|         { | ||||
|             if (sizeof(DateTime) > 16) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128)); | ||||
|             if (sizeof(TimeSpan) > 16) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128)); | ||||
|             if (sizeof(Guid) > 16) throw new InvalidOperationException(nameof(Guid) + " was unexpectedly too big for " + nameof(DiscriminatedUnion128)); | ||||
|         } | ||||
| #endif | ||||
|         [FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64 | ||||
| 
 | ||||
|         /// <summary>The value typed as Int64</summary> | ||||
|         [FieldOffset(8)] public readonly long Int64; | ||||
|         /// <summary>The value typed as UInt64</summary> | ||||
|         [FieldOffset(8)] public readonly ulong UInt64; | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(8)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(8)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(8)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(8)] public readonly float Single; | ||||
|         /// <summary>The value typed as Double</summary> | ||||
|         [FieldOffset(8)] public readonly double Double; | ||||
|         /// <summary>The value typed as DateTime</summary> | ||||
|         [FieldOffset(8)] public readonly DateTime DateTime; | ||||
|         /// <summary>The value typed as TimeSpan</summary> | ||||
|         [FieldOffset(8)] public readonly TimeSpan TimeSpan; | ||||
|         /// <summary>The value typed as Guid</summary> | ||||
|         [FieldOffset(8)] public readonly Guid Guid; | ||||
| 
 | ||||
|         private DiscriminatedUnion128(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, long value) : this(discriminator) { Int64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, ulong value) : this(discriminator) { UInt64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, double value) : this(discriminator) { Double = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion128(int discriminator, Guid? value) : this(value.HasValue ? discriminator : 0) { Guid = value.GetValueOrDefault(); } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion128 value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion64Object | ||||
|     { | ||||
| #if !FEAT_SAFE | ||||
|         unsafe static DiscriminatedUnion64Object() | ||||
|         { | ||||
|             if (sizeof(DateTime) > 8) throw new InvalidOperationException(nameof(DateTime) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64Object)); | ||||
|             if (sizeof(TimeSpan) > 8) throw new InvalidOperationException(nameof(TimeSpan) + " was unexpectedly too big for " + nameof(DiscriminatedUnion64Object)); | ||||
|         } | ||||
| #endif | ||||
|         [FieldOffset(0)] private readonly int _discriminator;  // note that we can't pack further because Object needs x8 alignment/padding on x64 | ||||
| 
 | ||||
|         /// <summary>The value typed as Int64</summary> | ||||
|         [FieldOffset(8)] public readonly long Int64; | ||||
|         /// <summary>The value typed as UInt64</summary> | ||||
|         [FieldOffset(8)] public readonly ulong UInt64; | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(8)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(8)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(8)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(8)] public readonly float Single; | ||||
|         /// <summary>The value typed as Double</summary> | ||||
|         [FieldOffset(8)] public readonly double Double; | ||||
|         /// <summary>The value typed as DateTime</summary> | ||||
|         [FieldOffset(8)] public readonly DateTime DateTime; | ||||
|         /// <summary>The value typed as TimeSpan</summary> | ||||
|         [FieldOffset(8)] public readonly TimeSpan TimeSpan; | ||||
|         /// <summary>The value typed as Object</summary> | ||||
|         [FieldOffset(16)] public readonly object Object; | ||||
| 
 | ||||
|         private DiscriminatedUnion64Object(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, long value) : this(discriminator) { Int64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, ulong value) : this(discriminator) { UInt64 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, double value) : this(discriminator) { Double = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, DateTime? value) : this(value.HasValue ? discriminator: 0) { DateTime = value.GetValueOrDefault(); } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion64Object(int discriminator, TimeSpan? value) : this(value.HasValue ? discriminator : 0) { TimeSpan = value.GetValueOrDefault(); } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion64Object value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion32 | ||||
|     { | ||||
|         [FieldOffset(0)] private readonly int _discriminator; | ||||
| 
 | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(4)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(4)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(4)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(4)] public readonly float Single; | ||||
| 
 | ||||
|         private DiscriminatedUnion32(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion32 value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary>Represent multiple types as a union; this is used as part of OneOf - | ||||
|     /// note that it is the caller's responsbility to only read/write the value as the same type</summary> | ||||
|     [StructLayout(LayoutKind.Explicit)] | ||||
|     public readonly partial struct DiscriminatedUnion32Object | ||||
|     { | ||||
|         [FieldOffset(0)] private readonly int _discriminator; | ||||
| 
 | ||||
|         /// <summary>The value typed as Int32</summary> | ||||
|         [FieldOffset(4)] public readonly int Int32; | ||||
|         /// <summary>The value typed as UInt32</summary> | ||||
|         [FieldOffset(4)] public readonly uint UInt32; | ||||
|         /// <summary>The value typed as Boolean</summary> | ||||
|         [FieldOffset(4)] public readonly bool Boolean; | ||||
|         /// <summary>The value typed as Single</summary> | ||||
|         [FieldOffset(4)] public readonly float Single; | ||||
|         /// <summary>The value typed as Object</summary> | ||||
|         [FieldOffset(8)] public readonly object Object; | ||||
| 
 | ||||
|         private DiscriminatedUnion32Object(int discriminator) : this() | ||||
|         { | ||||
|             _discriminator = discriminator; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Indicates whether the specified discriminator is assigned</summary> | ||||
|         public bool Is(int discriminator) => _discriminator == discriminator; | ||||
| 
 | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32Object(int discriminator, int value) : this(discriminator) { Int32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32Object(int discriminator, uint value) : this(discriminator) { UInt32 = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32Object(int discriminator, float value) : this(discriminator) { Single = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32Object(int discriminator, bool value) : this(discriminator) { Boolean = value; } | ||||
|         /// <summary>Create a new discriminated union value</summary> | ||||
|         public DiscriminatedUnion32Object(int discriminator, object value) : this(value != null ? discriminator : 0) { Object = value; } | ||||
| 
 | ||||
|         /// <summary>Reset a value if the specified discriminator is assigned</summary> | ||||
|         public static void Reset(ref DiscriminatedUnion32Object value, int discriminator) | ||||
|         { | ||||
|             if (value.Discriminator == discriminator) value = default; | ||||
|         } | ||||
|         /// <summary>The discriminator value</summary> | ||||
|         public int Discriminator => _discriminator; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: ab51817e163a1144bb8518368ba0a465 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,284 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using ProtoBuf.Meta; | ||||
| using System.Collections; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Simple base class for supporting unexpected fields allowing | ||||
|     /// for loss-less round-tips/merge, even if the data is not understod. | ||||
|     /// The additional fields are (by default) stored in-memory in a buffer. | ||||
|     /// </summary> | ||||
|     /// <remarks>As an example of an alternative implementation, you might | ||||
|     /// choose to use the file system (temporary files) as the back-end, tracking | ||||
|     /// only the paths [such an object would ideally be IDisposable and use | ||||
|     /// a finalizer to ensure that the files are removed].</remarks> | ||||
|     /// <seealso cref="IExtensible"/> | ||||
|     public abstract class Extensible : IExtensible | ||||
|     { | ||||
|         // note: not marked ProtoContract - no local state, and can't  | ||||
|         // predict sub-classes | ||||
| 
 | ||||
|         private IExtension extensionObject; | ||||
| 
 | ||||
|         IExtension IExtensible.GetExtensionObject(bool createIfMissing) | ||||
|         { | ||||
|             return GetExtensionObject(createIfMissing); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Retrieves the <see cref="IExtension">extension</see> object for the current | ||||
|         /// instance, optionally creating it if it does not already exist. | ||||
|         /// </summary> | ||||
|         /// <param name="createIfMissing">Should a new extension object be | ||||
|         /// created if it does not already exist?</param> | ||||
|         /// <returns>The extension object if it exists (or was created), or null | ||||
|         /// if the extension object does not exist or is not available.</returns> | ||||
|         /// <remarks>The <c>createIfMissing</c> argument is false during serialization, | ||||
|         /// and true during deserialization upon encountering unexpected fields.</remarks> | ||||
|         protected virtual IExtension GetExtensionObject(bool createIfMissing) | ||||
|         { | ||||
|             return GetExtensionObject(ref extensionObject, createIfMissing); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Provides a simple, default implementation for <see cref="IExtension">extension</see> support, | ||||
|         /// optionally creating it if it does not already exist. Designed to be called by | ||||
|         /// classes implementing <see cref="IExtensible"/>. | ||||
|         /// </summary> | ||||
|         /// <param name="createIfMissing">Should a new extension object be | ||||
|         /// created if it does not already exist?</param> | ||||
|         /// <param name="extensionObject">The extension field to check (and possibly update).</param> | ||||
|         /// <returns>The extension object if it exists (or was created), or null | ||||
|         /// if the extension object does not exist or is not available.</returns> | ||||
|         /// <remarks>The <c>createIfMissing</c> argument is false during serialization, | ||||
|         /// and true during deserialization upon encountering unexpected fields.</remarks> | ||||
|         public static IExtension GetExtensionObject(ref IExtension extensionObject, bool createIfMissing) | ||||
|         { | ||||
|             if (createIfMissing && extensionObject == null) | ||||
|             { | ||||
|                 extensionObject = new BufferExtension(); | ||||
|             } | ||||
|             return extensionObject; | ||||
|         } | ||||
| 
 | ||||
| #if !NO_RUNTIME | ||||
|         /// <summary> | ||||
|         /// Appends the value as an additional (unexpected) data-field for the instance. | ||||
|         /// Note that for non-repeated sub-objects, this equates to a merge operation; | ||||
|         /// for repeated sub-objects this adds a new instance to the set; for simple | ||||
|         /// values the new value supercedes the old value. | ||||
|         /// </summary> | ||||
|         /// <remarks>Note that appending a value does not remove the old value from | ||||
|         /// the stream; avoid repeatedly appending values for the same field.</remarks> | ||||
|         /// <typeparam name="TValue">The type of the value to append.</typeparam> | ||||
|         /// <param name="instance">The extensible object to append the value to.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="value">The value to append.</param> | ||||
|         public static void AppendValue<TValue>(IExtensible instance, int tag, TValue value) | ||||
|         { | ||||
|             AppendValue<TValue>(instance, tag, DataFormat.Default, value); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Appends the value as an additional (unexpected) data-field for the instance. | ||||
|         /// Note that for non-repeated sub-objects, this equates to a merge operation; | ||||
|         /// for repeated sub-objects this adds a new instance to the set; for simple | ||||
|         /// values the new value supercedes the old value. | ||||
|         /// </summary> | ||||
|         /// <remarks>Note that appending a value does not remove the old value from | ||||
|         /// the stream; avoid repeatedly appending values for the same field.</remarks> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="format">The data-format to use when encoding the value.</param> | ||||
|         /// <param name="instance">The extensible object to append the value to.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="value">The value to append.</param> | ||||
|         public static void AppendValue<TValue>(IExtensible instance, int tag, DataFormat format, TValue value) | ||||
|         { | ||||
|             ExtensibleUtil.AppendExtendValue(RuntimeTypeModel.Default, instance, tag, format, value); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned is the composed value after merging any duplicated content; if the | ||||
|         /// value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <returns>The effective value of the field, or the default value if not found.</returns> | ||||
|         public static TValue GetValue<TValue>(IExtensible instance, int tag) | ||||
|         { | ||||
|             return GetValue<TValue>(instance, tag, DataFormat.Default); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned is the composed value after merging any duplicated content; if the | ||||
|         /// value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <returns>The effective value of the field, or the default value if not found.</returns> | ||||
|         public static TValue GetValue<TValue>(IExtensible instance, int tag, DataFormat format) | ||||
|         { | ||||
|             TryGetValue<TValue>(instance, tag, format, out TValue value); | ||||
|             return value; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned (in "value") is the composed value after merging any duplicated content; | ||||
|         /// if the value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="value">The effective value of the field, or the default value if not found.</param> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <returns>True if data for the field was present, false otherwise.</returns> | ||||
|         public static bool TryGetValue<TValue>(IExtensible instance, int tag, out TValue value) | ||||
|         { | ||||
|             return TryGetValue<TValue>(instance, tag, DataFormat.Default, out value); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned (in "value") is the composed value after merging any duplicated content; | ||||
|         /// if the value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="value">The effective value of the field, or the default value if not found.</param> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <returns>True if data for the field was present, false otherwise.</returns> | ||||
|         public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, out TValue value) | ||||
|         { | ||||
|             return TryGetValue<TValue>(instance, tag, format, false, out value); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned (in "value") is the composed value after merging any duplicated content; | ||||
|         /// if the value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="value">The effective value of the field, or the default value if not found.</param> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <param name="allowDefinedTag">Allow tags that are present as part of the definition; for example, to query unknown enum values.</param> | ||||
|         /// <returns>True if data for the field was present, false otherwise.</returns> | ||||
|         public static bool TryGetValue<TValue>(IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out TValue value) | ||||
|         { | ||||
|             value = default; | ||||
|             bool set = false; | ||||
|             foreach (TValue val in ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, true, allowDefinedTag)) | ||||
|             { | ||||
|                 // expecting at most one yield... | ||||
|                 // but don't break; need to read entire stream | ||||
|                 value = val; | ||||
|                 set = true; | ||||
|             } | ||||
| 
 | ||||
|             return set; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated" | ||||
|         /// (list) fields. | ||||
|         /// </summary> | ||||
|         /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <returns>An enumerator that yields each occurrence of the field.</returns> | ||||
|         public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag) | ||||
|         { | ||||
|             return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, DataFormat.Default, false, false); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated" | ||||
|         /// (list) fields. | ||||
|         /// </summary> | ||||
|         /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks> | ||||
|         /// <typeparam name="TValue">The data-type of the field.</typeparam> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <returns>An enumerator that yields each occurrence of the field.</returns> | ||||
|         public static IEnumerable<TValue> GetValues<TValue>(IExtensible instance, int tag, DataFormat format) | ||||
|         { | ||||
|             return ExtensibleUtil.GetExtendedValues<TValue>(instance, tag, format, false, false); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// The value returned (in "value") is the composed value after merging any duplicated content; | ||||
|         /// if the value is "repeated" (a list), then use GetValues instead. | ||||
|         /// </summary> | ||||
|         /// <param name="type">The data-type of the field.</param> | ||||
|         /// <param name="model">The model to use for configuration.</param> | ||||
|         /// <param name="value">The effective value of the field, or the default value if not found.</param> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <param name="allowDefinedTag">Allow tags that are present as part of the definition; for example, to query unknown enum values.</param> | ||||
|         /// <returns>True if data for the field was present, false otherwise.</returns> | ||||
|         public static bool TryGetValue(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool allowDefinedTag, out object value) | ||||
|         { | ||||
|             value = null; | ||||
|             bool set = false; | ||||
|             foreach (object val in ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, true, allowDefinedTag)) | ||||
|             { | ||||
|                 // expecting at most one yield... | ||||
|                 // but don't break; need to read entire stream | ||||
|                 value = val; | ||||
|                 set = true; | ||||
|             } | ||||
| 
 | ||||
|             return set; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Queries an extensible object for an additional (unexpected) data-field for the instance. | ||||
|         /// Each occurrence of the field is yielded separately, making this usage suitable for "repeated" | ||||
|         /// (list) fields. | ||||
|         /// </summary> | ||||
|         /// <remarks>The extended data is processed lazily as the enumerator is iterated.</remarks> | ||||
|         /// <param name="model">The model to use for configuration.</param> | ||||
|         /// <param name="type">The data-type of the field.</param> | ||||
|         /// <param name="instance">The extensible object to obtain the value from.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="format">The data-format to use when decoding the value.</param> | ||||
|         /// <returns>An enumerator that yields each occurrence of the field.</returns> | ||||
|         public static IEnumerable GetValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format) | ||||
|         { | ||||
|             return ExtensibleUtil.GetExtendedValues(model, type, instance, tag, format, false, false); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Appends the value as an additional (unexpected) data-field for the instance. | ||||
|         /// Note that for non-repeated sub-objects, this equates to a merge operation; | ||||
|         /// for repeated sub-objects this adds a new instance to the set; for simple | ||||
|         /// values the new value supercedes the old value. | ||||
|         /// </summary> | ||||
|         /// <remarks>Note that appending a value does not remove the old value from | ||||
|         /// the stream; avoid repeatedly appending values for the same field.</remarks> | ||||
|         /// <param name="model">The model to use for configuration.</param> | ||||
|         /// <param name="format">The data-format to use when encoding the value.</param> | ||||
|         /// <param name="instance">The extensible object to append the value to.</param> | ||||
|         /// <param name="tag">The field identifier; the tag should not be defined as a known data-field for the instance.</param> | ||||
|         /// <param name="value">The value to append.</param> | ||||
|         public static void AppendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value) | ||||
|         { | ||||
|             ExtensibleUtil.AppendExtendValue(model, instance, tag, format, value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: fc24b62dbd0b19642bce397e2b061aa0 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,118 @@ | |||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using ProtoBuf.Meta; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// This class acts as an internal wrapper allowing us to do a dynamic | ||||
|     /// methodinfo invoke; an't put into Serializer as don't want on public | ||||
|     /// API; can't put into Serializer<T> since we need to invoke | ||||
|     /// across classes | ||||
|     /// </summary> | ||||
|     internal static class ExtensibleUtil | ||||
|     { | ||||
| 
 | ||||
| #if !NO_RUNTIME | ||||
|         /// <summary> | ||||
|         /// All this does is call GetExtendedValuesTyped with the correct type for "instance"; | ||||
|         /// this ensures that we don't get issues with subclasses declaring conflicting types - | ||||
|         /// the caller must respect the fields defined for the type they pass in. | ||||
|         /// </summary> | ||||
|         internal static IEnumerable<TValue> GetExtendedValues<TValue>(IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag) | ||||
|         { | ||||
|             foreach (TValue value in GetExtendedValues(RuntimeTypeModel.Default, typeof(TValue), instance, tag, format, singleton, allowDefinedTag)) | ||||
|             { | ||||
|                 yield return value; | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|         /// <summary> | ||||
|         /// All this does is call GetExtendedValuesTyped with the correct type for "instance"; | ||||
|         /// this ensures that we don't get issues with subclasses declaring conflicting types - | ||||
|         /// the caller must respect the fields defined for the type they pass in. | ||||
|         /// </summary> | ||||
|         internal static IEnumerable GetExtendedValues(TypeModel model, Type type, IExtensible instance, int tag, DataFormat format, bool singleton, bool allowDefinedTag) | ||||
|         { | ||||
|             if (instance == null) throw new ArgumentNullException(nameof(instance)); | ||||
|             if (tag <= 0) throw new ArgumentOutOfRangeException(nameof(tag)); | ||||
|             IExtension extn = instance.GetExtensionObject(false); | ||||
| 
 | ||||
|             if (extn == null) | ||||
|             { | ||||
|                 yield break; | ||||
|             } | ||||
| 
 | ||||
|             Stream stream = extn.BeginQuery(); | ||||
|             object value = null; | ||||
|             ProtoReader reader = null; | ||||
|             try | ||||
|             { | ||||
|                 SerializationContext ctx = new SerializationContext(); | ||||
|                 reader = ProtoReader.Create(stream, model, ctx, ProtoReader.TO_EOF); | ||||
|                 while (model.TryDeserializeAuxiliaryType(reader, format, tag, type, ref value, true, true, false, false, null) && value != null) | ||||
|                 { | ||||
|                     if (!singleton) | ||||
|                     { | ||||
|                         yield return value; | ||||
| 
 | ||||
|                         value = null; // fresh item each time | ||||
|                     } | ||||
|                 } | ||||
|                 if (singleton && value != null) | ||||
|                 { | ||||
|                     yield return value; | ||||
|                 } | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 ProtoReader.Recycle(reader); | ||||
|                 extn.EndQuery(stream); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal static void AppendExtendValue(TypeModel model, IExtensible instance, int tag, DataFormat format, object value) | ||||
|         { | ||||
|             if (instance == null) throw new ArgumentNullException(nameof(instance)); | ||||
|             if (value == null) throw new ArgumentNullException(nameof(value)); | ||||
| 
 | ||||
|             // TODO | ||||
|             //model.CheckTagNotInUse(tag); | ||||
| 
 | ||||
|             // obtain the extension object and prepare to write | ||||
|             IExtension extn = instance.GetExtensionObject(true); | ||||
|             if (extn == null) throw new InvalidOperationException("No extension object available; appended data would be lost."); | ||||
|             bool commit = false; | ||||
|             Stream stream = extn.BeginAppend(); | ||||
|             try | ||||
|             { | ||||
|                 using (ProtoWriter writer = ProtoWriter.Create(stream, model, null)) | ||||
|                 { | ||||
|                     model.TrySerializeAuxiliaryType(writer, null, format, tag, value, false, null); | ||||
|                     writer.Close(); | ||||
|                 } | ||||
|                 commit = true; | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 extn.EndAppend(stream, commit); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //        /// <summary> | ||||
|         //        /// Stores the given value into the instance's stream; the serializer | ||||
|         //        /// is inferred from TValue and format. | ||||
|         //        /// </summary> | ||||
|         //        /// <remarks>Needs to be public to be callable thru reflection in Silverlight</remarks> | ||||
|         //        public static void AppendExtendValueTyped<TSource, TValue>( | ||||
|         //            TypeModel model, TSource instance, int tag, DataFormat format, TValue value) | ||||
|         //            where TSource : class, IExtensible | ||||
|         //        { | ||||
|         //            AppendExtendValue(model, instance, tag, format, value); | ||||
|         //        } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: dc71d3f5e8f25ad41bb04ea933cee56e | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: c110f96e5d6da4f498bcb6d5fa673be7 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,638 @@ | |||
|  | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Text; | ||||
| #if COREFX | ||||
| using System.Linq; | ||||
| #endif | ||||
| #if PROFILE259 | ||||
| using System.Reflection; | ||||
| using System.Linq; | ||||
| #else | ||||
| using System.Reflection; | ||||
| #endif | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Not all frameworks are created equal (fx1.1 vs fx2.0, | ||||
|     /// micro-framework, compact-framework, | ||||
|     /// silverlight, etc). This class simply wraps up a few things that would | ||||
|     /// otherwise make the real code unnecessarily messy, providing fallback | ||||
|     /// implementations if necessary. | ||||
|     /// </summary> | ||||
|     internal sealed class Helpers | ||||
|     { | ||||
|         private Helpers() { } | ||||
| 
 | ||||
|         public static StringBuilder AppendLine(StringBuilder builder) | ||||
|         { | ||||
|             return builder.AppendLine(); | ||||
|         } | ||||
| 
 | ||||
|         [System.Diagnostics.Conditional("DEBUG")] | ||||
|         public static void DebugWriteLine(string message, object obj) | ||||
|         { | ||||
| #if DEBUG | ||||
|             string suffix; | ||||
|             try | ||||
|             { | ||||
|                 suffix = obj == null ? "(null)" : obj.ToString(); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 suffix = "(exception)"; | ||||
|             } | ||||
|             DebugWriteLine(message + ": " + suffix); | ||||
| #endif | ||||
|         } | ||||
|         [System.Diagnostics.Conditional("DEBUG")] | ||||
|         public static void DebugWriteLine(string message) | ||||
|         { | ||||
| #if DEBUG | ||||
|             System.Diagnostics.Debug.WriteLine(message); | ||||
| #endif | ||||
|         } | ||||
|         [System.Diagnostics.Conditional("TRACE")] | ||||
|         public static void TraceWriteLine(string message) | ||||
|         { | ||||
| #if TRACE | ||||
| #if CF2 || PORTABLE || COREFX || PROFILE259 | ||||
| 			System.Diagnostics.Debug.WriteLine(message); | ||||
| #else | ||||
|             System.Diagnostics.Trace.WriteLine(message); | ||||
| #endif | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         [System.Diagnostics.Conditional("DEBUG")] | ||||
|         public static void DebugAssert(bool condition, string message) | ||||
|         { | ||||
| #if DEBUG | ||||
|             if (!condition) | ||||
|             { | ||||
|                 System.Diagnostics.Debug.Assert(false, message); | ||||
|             } | ||||
| #endif | ||||
|         } | ||||
|         [System.Diagnostics.Conditional("DEBUG")] | ||||
|         public static void DebugAssert(bool condition, string message, params object[] args) | ||||
|         { | ||||
| #if DEBUG | ||||
|             if (!condition) DebugAssert(false, string.Format(message, args)); | ||||
| #endif | ||||
|         } | ||||
|         [System.Diagnostics.Conditional("DEBUG")] | ||||
|         public static void DebugAssert(bool condition) | ||||
|         { | ||||
| #if DEBUG    | ||||
|             if (!condition && System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break(); | ||||
|             System.Diagnostics.Debug.Assert(condition); | ||||
| #endif | ||||
|         } | ||||
| #if !NO_RUNTIME | ||||
|         public static void Sort(int[] keys, object[] values) | ||||
|         { | ||||
|             // bubble-sort; it'll work on MF, has small code, | ||||
|             // and works well-enough for our sizes. This approach | ||||
|             // also allows us to do `int` compares without having | ||||
|             // to go via IComparable etc, so win:win | ||||
|             bool swapped; | ||||
|             do | ||||
|             { | ||||
|                 swapped = false; | ||||
|                 for (int i = 1; i < keys.Length; i++) | ||||
|                 { | ||||
|                     if (keys[i - 1] > keys[i]) | ||||
|                     { | ||||
|                         int tmpKey = keys[i]; | ||||
|                         keys[i] = keys[i - 1]; | ||||
|                         keys[i - 1] = tmpKey; | ||||
|                         object tmpValue = values[i]; | ||||
|                         values[i] = values[i - 1]; | ||||
|                         values[i - 1] = tmpValue; | ||||
|                         swapped = true; | ||||
|                     } | ||||
|                 } | ||||
|             } while (swapped); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
| #if COREFX | ||||
| 		internal static MemberInfo GetInstanceMember(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             var members = declaringType.AsType().GetMember(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | ||||
|             switch(members.Length) | ||||
|             { | ||||
|                 case 0: return null; | ||||
|                 case 1: return members[0]; | ||||
|                 default: throw new AmbiguousMatchException(name); | ||||
|             } | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name) | ||||
|         { | ||||
|             foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) | ||||
|             { | ||||
|                 if (method.Name == name) return method; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             return GetInstanceMethod(declaringType.AsType(), name); ; | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name) | ||||
|         { | ||||
|             foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) | ||||
|             { | ||||
|                 if (method.Name == name) return method; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         internal static MethodInfo GetStaticMethod(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             return GetStaticMethod(declaringType.AsType(), name); | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes) | ||||
|         { | ||||
|             foreach(MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) | ||||
|             { | ||||
|                 if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] parameterTypes) | ||||
|         { | ||||
|             foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) | ||||
|             { | ||||
|                 if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name, Type[] types) | ||||
|         { | ||||
|             return GetInstanceMethod(declaringType.AsType(), name, types); | ||||
|         } | ||||
| #elif PROFILE259 | ||||
|         internal static MemberInfo GetInstanceMember(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             IEnumerable<MemberInfo> members = declaringType.DeclaredMembers; | ||||
|             IList<MemberInfo> found = new List<MemberInfo>(); | ||||
|             foreach (MemberInfo member in members) | ||||
|             { | ||||
|                 if (member.Name.Equals(name)) | ||||
|                 { | ||||
|                     found.Add(member); | ||||
|                 } | ||||
|             } | ||||
|             switch (found.Count) | ||||
|             { | ||||
|                 case 0: return null; | ||||
|                 case 1: return found.First(); | ||||
|                 default: throw new AmbiguousMatchException(name); | ||||
|             } | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name) | ||||
|         { | ||||
|             var methods = declaringType.GetRuntimeMethods(); | ||||
|             foreach (MethodInfo method in methods) | ||||
|             { | ||||
|                 if (method.Name == name) | ||||
|                 { | ||||
|                     return method; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             return GetInstanceMethod(declaringType.AsType(), name); ; | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name) | ||||
|         { | ||||
|             var methods = declaringType.GetRuntimeMethods(); | ||||
|             foreach (MethodInfo method in methods) | ||||
|             { | ||||
|                 if (method.Name == name) | ||||
|                 { | ||||
|                     return method; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
| 
 | ||||
|         internal static MethodInfo GetStaticMethod(TypeInfo declaringType, string name) | ||||
|         { | ||||
|             return GetStaticMethod(declaringType.AsType(), name); | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes) | ||||
|         { | ||||
|             var methods = declaringType.GetRuntimeMethods(); | ||||
|             foreach (MethodInfo method in methods) | ||||
|             { | ||||
|                 if (method.Name == name && | ||||
|                     IsMatch(method.GetParameters(), parameterTypes)) | ||||
|                 { | ||||
|                     return method; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] parameterTypes) | ||||
|         { | ||||
|             var methods = declaringType.GetRuntimeMethods(); | ||||
|             foreach (MethodInfo method in methods) | ||||
|             { | ||||
|                 if (method.Name == name && | ||||
|                     IsMatch(method.GetParameters(), parameterTypes)) | ||||
|                 { | ||||
|                     return method; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(TypeInfo declaringType, string name, Type[] types) | ||||
|         { | ||||
|             return GetInstanceMethod(declaringType.AsType(), name, types); | ||||
|         } | ||||
| #else | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name) | ||||
|         { | ||||
|             return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name) | ||||
|         { | ||||
|             return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); | ||||
|         } | ||||
|         internal static MethodInfo GetStaticMethod(Type declaringType, string name, Type[] parameterTypes) | ||||
|         { | ||||
| #if PORTABLE | ||||
|             foreach (MethodInfo method in declaringType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) | ||||
|             { | ||||
|                 if (method.Name == name && IsMatch(method.GetParameters(), parameterTypes)) return method; | ||||
|             } | ||||
|             return null; | ||||
| #else | ||||
|             return declaringType.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); | ||||
| #endif | ||||
|         } | ||||
|         internal static MethodInfo GetInstanceMethod(Type declaringType, string name, Type[] types) | ||||
|         { | ||||
|             if (types == null) types = EmptyTypes; | ||||
| #if PORTABLE || COREFX | ||||
|             MethodInfo method = declaringType.GetMethod(name, types); | ||||
|             if (method != null && method.IsStatic) method = null; | ||||
|             return method; | ||||
| #else | ||||
|             return declaringType.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, | ||||
|                 null, types, null); | ||||
| #endif | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
|         internal static bool IsSubclassOf(Type type, Type baseClass) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().IsSubclassOf(baseClass); | ||||
| #else | ||||
|             return type.IsSubclassOf(baseClass); | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         public readonly static Type[] EmptyTypes = | ||||
| #if PORTABLE || CF2 || CF35 || PROFILE259 | ||||
|             new Type[0]; | ||||
| #else | ||||
|             Type.EmptyTypes; | ||||
| #endif | ||||
| 
 | ||||
| #if COREFX || PROFILE259 | ||||
|         private static readonly Type[] knownTypes = new Type[] { | ||||
|                 typeof(bool), typeof(char), typeof(sbyte), typeof(byte), | ||||
|                 typeof(short), typeof(ushort), typeof(int), typeof(uint), | ||||
|                 typeof(long), typeof(ulong), typeof(float), typeof(double), | ||||
|                 typeof(decimal), typeof(string), | ||||
|                 typeof(DateTime), typeof(TimeSpan), typeof(Guid), typeof(Uri), | ||||
|                 typeof(byte[]), typeof(Type)}; | ||||
|         private static readonly ProtoTypeCode[] knownCodes = new ProtoTypeCode[] { | ||||
|             ProtoTypeCode.Boolean, ProtoTypeCode.Char, ProtoTypeCode.SByte, ProtoTypeCode.Byte, | ||||
|             ProtoTypeCode.Int16, ProtoTypeCode.UInt16, ProtoTypeCode.Int32, ProtoTypeCode.UInt32, | ||||
|             ProtoTypeCode.Int64, ProtoTypeCode.UInt64, ProtoTypeCode.Single, ProtoTypeCode.Double, | ||||
|             ProtoTypeCode.Decimal, ProtoTypeCode.String, | ||||
|             ProtoTypeCode.DateTime, ProtoTypeCode.TimeSpan, ProtoTypeCode.Guid, ProtoTypeCode.Uri, | ||||
|             ProtoTypeCode.ByteArray, ProtoTypeCode.Type | ||||
|         }; | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|         public static ProtoTypeCode GetTypeCode(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             if (IsEnum(type)) | ||||
|             { | ||||
|                 type = Enum.GetUnderlyingType(type); | ||||
|             } | ||||
|             int idx = Array.IndexOf<Type>(knownTypes, type); | ||||
|             if (idx >= 0) return knownCodes[idx]; | ||||
|             return type == null ? ProtoTypeCode.Empty : ProtoTypeCode.Unknown; | ||||
| #else | ||||
|             TypeCode code = Type.GetTypeCode(type); | ||||
|             switch (code) | ||||
|             { | ||||
|                 case TypeCode.Empty: | ||||
|                 case TypeCode.Boolean: | ||||
|                 case TypeCode.Char: | ||||
|                 case TypeCode.SByte: | ||||
|                 case TypeCode.Byte: | ||||
|                 case TypeCode.Int16: | ||||
|                 case TypeCode.UInt16: | ||||
|                 case TypeCode.Int32: | ||||
|                 case TypeCode.UInt32: | ||||
|                 case TypeCode.Int64: | ||||
|                 case TypeCode.UInt64: | ||||
|                 case TypeCode.Single: | ||||
|                 case TypeCode.Double: | ||||
|                 case TypeCode.Decimal: | ||||
|                 case TypeCode.DateTime: | ||||
|                 case TypeCode.String: | ||||
|                     return (ProtoTypeCode)code; | ||||
|             } | ||||
|             if (type == typeof(TimeSpan)) return ProtoTypeCode.TimeSpan; | ||||
|             if (type == typeof(Guid)) return ProtoTypeCode.Guid; | ||||
|             if (type == typeof(Uri)) return ProtoTypeCode.Uri; | ||||
| #if PORTABLE | ||||
|             // In PCLs, the Uri type may not match (WinRT uses Internal/Uri, .Net uses System/Uri), so match on the full name instead | ||||
|             if (type.FullName == typeof(Uri).FullName) return ProtoTypeCode.Uri; | ||||
| #endif | ||||
|             if (type == typeof(byte[])) return ProtoTypeCode.ByteArray; | ||||
|             if (type == typeof(Type)) return ProtoTypeCode.Type; | ||||
| 
 | ||||
|             return ProtoTypeCode.Unknown; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         internal static Type GetUnderlyingType(Type type) | ||||
|         { | ||||
|             return Nullable.GetUnderlyingType(type); | ||||
|         } | ||||
| 
 | ||||
|         internal static bool IsValueType(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().IsValueType; | ||||
| #else | ||||
|             return type.IsValueType; | ||||
| #endif | ||||
|         } | ||||
|         internal static bool IsSealed(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().IsSealed; | ||||
| #else | ||||
|             return type.IsSealed; | ||||
| #endif | ||||
|         } | ||||
|         internal static bool IsClass(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().IsClass; | ||||
| #else | ||||
|             return type.IsClass; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         internal static bool IsEnum(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().IsEnum; | ||||
| #else | ||||
|             return type.IsEnum; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         internal static MethodInfo GetGetMethod(PropertyInfo property, bool nonPublic, bool allowInternal) | ||||
|         { | ||||
|             if (property == null) return null; | ||||
| #if COREFX || PROFILE259 | ||||
|             MethodInfo method = property.GetMethod; | ||||
|             if (!nonPublic && method != null && !method.IsPublic) method = null; | ||||
|             return method; | ||||
| #else | ||||
|             MethodInfo method = property.GetGetMethod(nonPublic); | ||||
|             if (method == null && !nonPublic && allowInternal) | ||||
|             { // could be "internal" or "protected internal"; look for a non-public, then back-check | ||||
|                 method = property.GetGetMethod(true); | ||||
|                 if (method == null && !(method.IsAssembly || method.IsFamilyOrAssembly)) | ||||
|                 { | ||||
|                     method = null; | ||||
|                 } | ||||
|             } | ||||
|             return method; | ||||
| #endif | ||||
|         } | ||||
|         internal static MethodInfo GetSetMethod(PropertyInfo property, bool nonPublic, bool allowInternal) | ||||
|         { | ||||
|             if (property == null) return null; | ||||
| #if COREFX || PROFILE259 | ||||
|             MethodInfo method = property.SetMethod; | ||||
|             if (!nonPublic && method != null && !method.IsPublic) method = null; | ||||
|             return method; | ||||
| #else | ||||
|             MethodInfo method = property.GetSetMethod(nonPublic); | ||||
|             if (method == null && !nonPublic && allowInternal) | ||||
|             { // could be "internal" or "protected internal"; look for a non-public, then back-check | ||||
|                 method = property.GetGetMethod(true); | ||||
|                 if (method == null && !(method.IsAssembly || method.IsFamilyOrAssembly)) | ||||
|                 { | ||||
|                     method = null; | ||||
|                 } | ||||
|             } | ||||
|             return method; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
| #if COREFX || PORTABLE || PROFILE259 | ||||
|         private static bool IsMatch(ParameterInfo[] parameters, Type[] parameterTypes) | ||||
|         { | ||||
|             if (parameterTypes == null) parameterTypes = EmptyTypes; | ||||
|             if (parameters.Length != parameterTypes.Length) return false; | ||||
|             for (int i = 0; i < parameters.Length; i++) | ||||
|             { | ||||
|                 if (parameters[i].ParameterType != parameterTypes[i]) return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| #endif | ||||
| #if COREFX || PROFILE259 | ||||
|         internal static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes, bool nonPublic) | ||||
|         { | ||||
|             return GetConstructor(type.GetTypeInfo(), parameterTypes, nonPublic); | ||||
|         } | ||||
|         internal static ConstructorInfo GetConstructor(TypeInfo type, Type[] parameterTypes, bool nonPublic) | ||||
|         { | ||||
|             return GetConstructors(type, nonPublic).SingleOrDefault(ctor => IsMatch(ctor.GetParameters(), parameterTypes)); | ||||
|         } | ||||
|         internal static ConstructorInfo[] GetConstructors(TypeInfo typeInfo, bool nonPublic) | ||||
|         { | ||||
|             return typeInfo.DeclaredConstructors.Where(c => !c.IsStatic && ((!nonPublic && c.IsPublic) || nonPublic)).ToArray(); | ||||
|         } | ||||
|         internal static PropertyInfo GetProperty(Type type, string name, bool nonPublic) | ||||
|         { | ||||
|             return GetProperty(type.GetTypeInfo(), name, nonPublic); | ||||
|         } | ||||
|         internal static PropertyInfo GetProperty(TypeInfo type, string name, bool nonPublic) | ||||
|         { | ||||
|             return type.GetDeclaredProperty(name); | ||||
|         } | ||||
| #else | ||||
| 
 | ||||
|         internal static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes, bool nonPublic) | ||||
|         { | ||||
| #if PORTABLE || COREFX | ||||
|             // pretty sure this will only ever return public, but... | ||||
|             ConstructorInfo ctor = type.GetConstructor(parameterTypes); | ||||
|             return (ctor != null && (nonPublic || ctor.IsPublic)) ? ctor : null; | ||||
| #else | ||||
|             return type.GetConstructor( | ||||
|                 nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | ||||
|                           : BindingFlags.Instance | BindingFlags.Public, | ||||
|                     null, parameterTypes, null); | ||||
| #endif | ||||
| 
 | ||||
|         } | ||||
|         internal static ConstructorInfo[] GetConstructors(Type type, bool nonPublic) | ||||
|         { | ||||
|             return type.GetConstructors( | ||||
|                 nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | ||||
|                           : BindingFlags.Instance | BindingFlags.Public); | ||||
|         } | ||||
|         internal static PropertyInfo GetProperty(Type type, string name, bool nonPublic) | ||||
|         { | ||||
|             return type.GetProperty(name, | ||||
|                 nonPublic ? BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | ||||
|                           : BindingFlags.Instance | BindingFlags.Public); | ||||
|         } | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
|         internal static object ParseEnum(Type type, string value) | ||||
|         { | ||||
|             return Enum.Parse(type, value, true); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         internal static MemberInfo[] GetInstanceFieldsAndProperties(Type type, bool publicOnly) | ||||
|         { | ||||
| #if PROFILE259 | ||||
|             var members = new List<MemberInfo>(); | ||||
|             foreach (FieldInfo field in type.GetRuntimeFields()) | ||||
|             { | ||||
|                 if (field.IsStatic) continue; | ||||
|                 if (field.IsPublic || !publicOnly) members.Add(field); | ||||
|             } | ||||
|             foreach (PropertyInfo prop in type.GetRuntimeProperties()) | ||||
|             { | ||||
|                 MethodInfo getter = Helpers.GetGetMethod(prop, true, true); | ||||
|                 if (getter == null || getter.IsStatic) continue; | ||||
|                 if (getter.IsPublic || !publicOnly) members.Add(prop); | ||||
|             } | ||||
|             return members.ToArray(); | ||||
| #else | ||||
|             BindingFlags flags = publicOnly ? BindingFlags.Public | BindingFlags.Instance : BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic; | ||||
|             PropertyInfo[] props = type.GetProperties(flags); | ||||
|             FieldInfo[] fields = type.GetFields(flags); | ||||
|             MemberInfo[] members = new MemberInfo[fields.Length + props.Length]; | ||||
|             props.CopyTo(members, 0); | ||||
|             fields.CopyTo(members, props.Length); | ||||
|             return members; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         internal static Type GetMemberType(MemberInfo member) | ||||
|         { | ||||
| #if PORTABLE || COREFX || PROFILE259 | ||||
|             if (member is PropertyInfo prop) return prop.PropertyType; | ||||
|             FieldInfo fld = member as FieldInfo; | ||||
|             return fld?.FieldType; | ||||
| #else | ||||
|             switch (member.MemberType) | ||||
|             { | ||||
|                 case MemberTypes.Field: return ((FieldInfo)member).FieldType; | ||||
|                 case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; | ||||
|                 default: return null; | ||||
|             } | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         internal static bool IsAssignableFrom(Type target, Type type) | ||||
|         { | ||||
| #if PROFILE259 | ||||
|             return target.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()); | ||||
| #else | ||||
|             return target.IsAssignableFrom(type); | ||||
| #endif | ||||
|         } | ||||
|         internal static Assembly GetAssembly(Type type) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
|             return type.GetTypeInfo().Assembly; | ||||
| #else | ||||
|             return type.Assembly; | ||||
| #endif | ||||
|         } | ||||
|         internal static byte[] GetBuffer(MemoryStream ms) | ||||
|         { | ||||
| #if COREFX | ||||
|             if(!ms.TryGetBuffer(out var segment)) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Unable to obtain underlying MemoryStream buffer"); | ||||
|             } else if(segment.Offset != 0) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Underlying MemoryStream buffer was not zero-offset"); | ||||
|             } else | ||||
|             { | ||||
|                 return segment.Array; | ||||
|             } | ||||
| #elif PORTABLE || PROFILE259 | ||||
|             return ms.ToArray(); | ||||
| #else | ||||
|             return ms.GetBuffer(); | ||||
| #endif | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// Intended to be a direct map to regular TypeCode, but: | ||||
|     /// - with missing types | ||||
|     /// - existing on WinRT | ||||
|     /// </summary> | ||||
|     internal enum ProtoTypeCode | ||||
|     { | ||||
|         Empty = 0, | ||||
|         Unknown = 1, // maps to TypeCode.Object | ||||
|         Boolean = 3, | ||||
|         Char = 4, | ||||
|         SByte = 5, | ||||
|         Byte = 6, | ||||
|         Int16 = 7, | ||||
|         UInt16 = 8, | ||||
|         Int32 = 9, | ||||
|         UInt32 = 10, | ||||
|         Int64 = 11, | ||||
|         UInt64 = 12, | ||||
|         Single = 13, | ||||
|         Double = 14, | ||||
|         Decimal = 15, | ||||
|         DateTime = 16, | ||||
|         String = 18, | ||||
| 
 | ||||
|         // additions | ||||
|         TimeSpan = 100, | ||||
|         ByteArray = 101, | ||||
|         Guid = 102, | ||||
|         Uri = 103, | ||||
|         Type = 104 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 227f762ea287cdf42a9293ea6c481ff8 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,23 @@ | |||
|  | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Indicates that the implementing type has support for protocol-buffer | ||||
|     /// <see cref="IExtension">extensions</see>. | ||||
|     /// </summary> | ||||
|     /// <remarks>Can be implemented by deriving from Extensible.</remarks> | ||||
|     public interface IExtensible | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Retrieves the <see cref="IExtension">extension</see> object for the current | ||||
|         /// instance, optionally creating it if it does not already exist. | ||||
|         /// </summary> | ||||
|         /// <param name="createIfMissing">Should a new extension object be | ||||
|         /// created if it does not already exist?</param> | ||||
|         /// <returns>The extension object if it exists (or was created), or null | ||||
|         /// if the extension object does not exist or is not available.</returns> | ||||
|         /// <remarks>The <c>createIfMissing</c> argument is false during serialization, | ||||
|         /// and true during deserialization upon encountering unexpected fields.</remarks> | ||||
|         IExtension GetExtensionObject(bool createIfMissing); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b9cd5092c5d6d9d4299fc0c88ebb9390 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,58 @@ | |||
|  | ||||
| using System.IO; | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Provides addition capability for supporting unexpected fields during | ||||
|     /// protocol-buffer serialization/deserialization. This allows for loss-less | ||||
|     /// round-trip/merge, even when the data is not fully understood. | ||||
|     /// </summary> | ||||
|     public interface IExtension | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Requests a stream into which any unexpected fields can be persisted. | ||||
|         /// </summary> | ||||
|         /// <returns>A new stream suitable for storing data.</returns> | ||||
|         Stream BeginAppend(); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Indicates that all unexpected fields have now been stored. The | ||||
|         /// implementing class is responsible for closing the stream. If | ||||
|         /// "commit" is not true the data may be discarded. | ||||
|         /// </summary> | ||||
|         /// <param name="stream">The stream originally obtained by BeginAppend.</param> | ||||
|         /// <param name="commit">True if the append operation completed successfully.</param> | ||||
|         void EndAppend(Stream stream, bool commit); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Requests a stream of the unexpected fields previously stored. | ||||
|         /// </summary> | ||||
|         /// <returns>A prepared stream of the unexpected fields.</returns> | ||||
|         Stream BeginQuery(); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Indicates that all unexpected fields have now been read. The | ||||
|         /// implementing class is responsible for closing the stream. | ||||
|         /// </summary> | ||||
|         /// <param name="stream">The stream originally obtained by BeginQuery.</param> | ||||
|         void EndQuery(Stream stream); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Requests the length of the raw binary stream; this is used | ||||
|         /// when serializing sub-entities to indicate the expected size. | ||||
|         /// </summary> | ||||
|         /// <returns>The length of the binary stream representing unexpected data.</returns> | ||||
|         int GetLength(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Provides the ability to remove all existing extension data | ||||
|     /// </summary> | ||||
|     public interface IExtensionResettable : IExtension | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Remove all existing extension data | ||||
|         /// </summary> | ||||
|         void Reset(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 8018fb363175787478148842225e7d16 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,13 @@ | |||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents the ability to deserialize values from an input of type <typeparamref name="TInput"/> | ||||
|     /// </summary> | ||||
|     public interface IProtoInput<TInput> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Deserialize a value from the input | ||||
|         /// </summary> | ||||
|         T Deserialize<T>(TInput source, T value = default, object userState = null); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a6514bacfd3143a49a027f15434586f7 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,55 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents the ability to serialize values to an output of type <typeparamref name="TOutput"/> | ||||
|     /// </summary> | ||||
|     public interface IProtoOutput<TOutput> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Serialize the provided value | ||||
|         /// </summary> | ||||
|         void Serialize<T>(TOutput destination, T value, object userState = null); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Represents the ability to serialize values to an output of type <typeparamref name="TOutput"/> | ||||
|     /// with pre-computation of the length | ||||
|     /// </summary> | ||||
|     public interface IMeasuredProtoOutput<TOutput> : IProtoOutput<TOutput> | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Measure the length of a value in advance of serialization | ||||
|         /// </summary> | ||||
|         MeasureState<T> Measure<T>(T value, object userState = null); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Serialize the previously measured value | ||||
|         /// </summary> | ||||
|         void Serialize<T>(MeasureState<T> measured, TOutput destination); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Represents the outcome of computing the length of an object; since this may have required computing lengths | ||||
|     /// for multiple objects, some metadata is retained so that a subsequent serialize operation using | ||||
|     /// this instance can re-use the previously calculated lengths. If the object state changes between the | ||||
|     /// measure and serialize operations, the behavior is undefined. | ||||
|     /// </summary> | ||||
|     public struct MeasureState<T> : IDisposable | ||||
|         // note: 2.4.* does not actually implement this API; | ||||
|         // it only advertises it for 3.* capability/feature-testing, i.e. | ||||
|         // callers can check whether a model implements | ||||
|         // IMeasuredProtoOutput<Foo>, and *work from that* | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// Releases all resources associated with this value | ||||
|         /// </summary> | ||||
|         public void Dispose() => throw new NotImplementedException(); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Gets the calculated length of this serialize operation, in bytes | ||||
|         /// </summary> | ||||
|         public long Length => throw new NotImplementedException(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 17c52d90924d69d4aaf31925ea2c90bf | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,29 @@ | |||
| namespace ProtoBuf | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Specifies the method used to infer field tags for members of the type | ||||
|     /// under consideration. Tags are deduced using the invariant alphabetic | ||||
|     /// sequence of the members' names; this makes implicit field tags very brittle, | ||||
|     /// and susceptible to changes such as field names (normally an isolated | ||||
|     /// change). | ||||
|     /// </summary> | ||||
|     public enum ImplicitFields | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// No members are serialized implicitly; all members require a suitable | ||||
|         /// attribute such as [ProtoMember]. This is the recmomended mode for | ||||
|         /// most scenarios. | ||||
|         /// </summary> | ||||
|         None = 0, | ||||
|         /// <summary> | ||||
|         /// Public properties and fields are eligible for implicit serialization; | ||||
|         /// this treats the public API as a contract. Ordering beings from ImplicitFirstTag. | ||||
|         /// </summary> | ||||
|         AllPublic = 1, | ||||
|         /// <summary> | ||||
|         /// Public and non-public fields are eligible for implicit serialization; | ||||
|         /// this acts as a state/implementation serializer. Ordering beings from ImplicitFirstTag. | ||||
|         /// </summary> | ||||
|         AllFields = 2 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b838f9e3c6536bc438e7c31f73c49160 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,44 @@ | |||
| //using System.Collections.Generic; | ||||
| 
 | ||||
| //namespace ProtoBuf | ||||
| //{ | ||||
| //    /// <summary> | ||||
| //    /// Mutable version of the common key/value pair struct; used during serialization. This type is intended for internal use only and should not | ||||
| //    /// be used by calling code; it is required to be public for implementation reasons. | ||||
| //    /// </summary> | ||||
| //    [ProtoContract] | ||||
| //    public struct KeyValuePairSurrogate<TKey,TValue> | ||||
| //    { | ||||
| //        private TKey key; | ||||
| //        private TValue value; | ||||
| //        /// <summary> | ||||
| //        /// The key of the pair. | ||||
| //        /// </summary> | ||||
| //        [ProtoMember(1, IsRequired = true)] | ||||
| //        public TKey Key { get { return key; } set { key = value; } } | ||||
| //        /// <summary> | ||||
| //        /// The value of the pair. | ||||
| //        /// </summary> | ||||
| //        [ProtoMember(2)] | ||||
| //        public TValue Value{ get { return value; } set { this.value = value; } } | ||||
| //        private KeyValuePairSurrogate(TKey key, TValue value) | ||||
| //        { | ||||
| //            this.key = key; | ||||
| //            this.value = value; | ||||
| //        } | ||||
| //        /// <summary> | ||||
| //        /// Convert a surrogate instance to a standard pair instance. | ||||
| //        /// </summary> | ||||
| //        public static implicit operator KeyValuePair<TKey, TValue> (KeyValuePairSurrogate<TKey, TValue> value) | ||||
| //        { | ||||
| //            return new KeyValuePair<TKey,TValue>(value.key, value.value); | ||||
| //        } | ||||
| //        /// <summary> | ||||
| //        /// Convert a standard pair instance to a surrogate instance. | ||||
| //        /// </summary> | ||||
| //        public static implicit operator KeyValuePairSurrogate<TKey, TValue>(KeyValuePair<TKey, TValue> value) | ||||
| //        { | ||||
| //            return new KeyValuePairSurrogate<TKey, TValue>(value.Key, value.Value); | ||||
| //        } | ||||
| //    } | ||||
| //} | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: b6221476e2339494cb5ee2bdc10ffd81 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a70a85c13dddce74d9a6395c440c9156 | ||||
| folderAsset: yes | ||||
| DefaultImporter: | ||||
|   externalObjects: {} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,108 @@ | |||
| #if !NO_RUNTIME | ||||
| using System; | ||||
| using System.Reflection; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     internal abstract class AttributeMap | ||||
|     { | ||||
| #if DEBUG | ||||
|         [Obsolete("Please use AttributeType instead")] | ||||
|         new public Type GetType() => AttributeType; | ||||
| #endif | ||||
|         public override string ToString() => AttributeType?.FullName ?? ""; | ||||
|         public abstract bool TryGet(string key, bool publicOnly, out object value); | ||||
|         public bool TryGet(string key, out object value) | ||||
|         { | ||||
|             return TryGet(key, true, out value); | ||||
|         } | ||||
|         public abstract Type AttributeType { get; } | ||||
|         public static AttributeMap[] Create(TypeModel model, Type type, bool inherit) | ||||
|         { | ||||
| 
 | ||||
| #if COREFX || PROFILE259 | ||||
| 			Attribute[] all = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.OfType<Attribute>(type.GetTypeInfo().GetCustomAttributes(inherit))); | ||||
| #else | ||||
|             object[] all = type.GetCustomAttributes(inherit); | ||||
| #endif | ||||
|             AttributeMap[] result = new AttributeMap[all.Length]; | ||||
|             for(int i = 0 ; i < all.Length ; i++) | ||||
|             { | ||||
|                 result[i] = new ReflectionAttributeMap((Attribute)all[i]); | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         public static AttributeMap[] Create(TypeModel model, MemberInfo member, bool inherit) | ||||
|         { | ||||
| 
 | ||||
| #if COREFX || PROFILE259 | ||||
| 			Attribute[] all = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.OfType<Attribute>(member.GetCustomAttributes(inherit))); | ||||
| #else | ||||
|             object[] all = member.GetCustomAttributes(inherit); | ||||
| #endif | ||||
|             AttributeMap[] result = new AttributeMap[all.Length]; | ||||
|             for(int i = 0 ; i < all.Length ; i++) | ||||
|             { | ||||
|                 result[i] = new ReflectionAttributeMap((Attribute)all[i]); | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|         public static AttributeMap[] Create(TypeModel model, Assembly assembly) | ||||
|         { | ||||
| #if COREFX || PROFILE259 | ||||
| 			Attribute[] all = System.Linq.Enumerable.ToArray(assembly.GetCustomAttributes()); | ||||
| #else | ||||
|             const bool inherit = false; | ||||
|             object[] all = assembly.GetCustomAttributes(inherit); | ||||
| #endif | ||||
|             AttributeMap[] result = new AttributeMap[all.Length]; | ||||
|             for(int i = 0 ; i < all.Length ; i++) | ||||
|             { | ||||
|                 result[i] = new ReflectionAttributeMap((Attribute)all[i]); | ||||
|             } | ||||
|             return result; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         public abstract object Target { get; } | ||||
| 
 | ||||
|         private sealed class ReflectionAttributeMap : AttributeMap | ||||
|         { | ||||
|             private readonly Attribute attribute; | ||||
| 
 | ||||
|             public ReflectionAttributeMap(Attribute attribute) | ||||
|             { | ||||
|                 this.attribute = attribute; | ||||
|             } | ||||
| 
 | ||||
|             public override object Target => attribute; | ||||
| 
 | ||||
|             public override Type AttributeType => attribute.GetType(); | ||||
| 
 | ||||
|             public override bool TryGet(string key, bool publicOnly, out object value) | ||||
|             { | ||||
|                 MemberInfo[] members = Helpers.GetInstanceFieldsAndProperties(attribute.GetType(), publicOnly); | ||||
|                 foreach (MemberInfo member in members) | ||||
|                 { | ||||
|                     if (string.Equals(member.Name, key, StringComparison.OrdinalIgnoreCase)) | ||||
|                     { | ||||
|                         if (member is PropertyInfo prop) { | ||||
|                             value = prop.GetValue(attribute, null); | ||||
|                             return true; | ||||
|                         } | ||||
|                         if (member is FieldInfo field) { | ||||
|                             value = field.GetValue(attribute); | ||||
|                             return true; | ||||
|                         } | ||||
| 
 | ||||
|                         throw new NotSupportedException(member.GetType().Name); | ||||
|                     } | ||||
|                 } | ||||
|                 value = null; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a3e64de7ef1358447843db562f78060f | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,267 @@ | |||
| using System; | ||||
| using System.Collections; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     internal sealed class MutableList : BasicList | ||||
|     { | ||||
|         /*  Like BasicList, but allows existing values to be changed | ||||
|          */ | ||||
|         public new object this[int index] | ||||
|         { | ||||
|             get { return head[index]; } | ||||
|             set { head[index] = value; } | ||||
|         } | ||||
|         public void RemoveLast() | ||||
|         { | ||||
|             head.RemoveLastWithMutate(); | ||||
|         } | ||||
| 
 | ||||
|         public void Clear() | ||||
|         { | ||||
|             head.Clear(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal class BasicList : IEnumerable | ||||
|     { | ||||
|         /* Requirements: | ||||
|          *   - Fast access by index | ||||
|          *   - Immutable in the tail, so a node can be read (iterated) without locking | ||||
|          *   - Lock-free tail handling must match the memory mode; struct for Node | ||||
|          *     wouldn't work as "read" would not be atomic | ||||
|          *   - Only operation required is append, but this shouldn't go out of its | ||||
|          *     way to be inefficient | ||||
|          *   - Assume that the caller is handling thread-safety (to co-ordinate with | ||||
|          *     other code); no attempt to be thread-safe | ||||
|          *   - Assume that the data is private; internal data structure is allowed to | ||||
|          *     be mutable (i.e. array is fine as long as we don't screw it up) | ||||
|          */ | ||||
|         private static readonly Node nil = new Node(null, 0); | ||||
| 
 | ||||
|         public void CopyTo(Array array, int offset) | ||||
|         { | ||||
|             head.CopyTo(array, offset); | ||||
|         } | ||||
| 
 | ||||
|         protected Node head = nil; | ||||
| 
 | ||||
|         public int Add(object value) | ||||
|         { | ||||
|             return (head = head.Append(value)).Length - 1; | ||||
|         } | ||||
| 
 | ||||
|         public object this[int index] => head[index]; | ||||
| 
 | ||||
|         //public object TryGet(int index) | ||||
|         //{ | ||||
|         //    return head.TryGet(index); | ||||
|         //} | ||||
| 
 | ||||
|         public void Trim() { head = head.Trim(); } | ||||
| 
 | ||||
|         public int Count => head.Length; | ||||
| 
 | ||||
|         IEnumerator IEnumerable.GetEnumerator() => new NodeEnumerator(head); | ||||
| 
 | ||||
|         public NodeEnumerator GetEnumerator() => new NodeEnumerator(head); | ||||
| 
 | ||||
|         public struct NodeEnumerator : IEnumerator | ||||
|         { | ||||
|             private int position; | ||||
|             private readonly Node node; | ||||
|             internal NodeEnumerator(Node node) | ||||
|             { | ||||
|                 this.position = -1; | ||||
|                 this.node = node; | ||||
|             } | ||||
|             void IEnumerator.Reset() { position = -1; } | ||||
|             public object Current { get { return node[position]; } } | ||||
|             public bool MoveNext() | ||||
|             { | ||||
|                 int len = node.Length; | ||||
|                 return (position <= len) && (++position < len); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal sealed class Node | ||||
|         { | ||||
|             public object this[int index] | ||||
|             { | ||||
|                 get | ||||
|                 { | ||||
|                     if (index >= 0 && index < length) | ||||
|                     { | ||||
|                         return data[index]; | ||||
|                     } | ||||
|                     throw new ArgumentOutOfRangeException(nameof(index)); | ||||
|                 } | ||||
|                 set | ||||
|                 { | ||||
|                     if (index >= 0 && index < length) | ||||
|                     { | ||||
|                         data[index] = value; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         throw new ArgumentOutOfRangeException(nameof(index)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             //public object TryGet(int index) | ||||
|             //{ | ||||
|             //    return (index >= 0 && index < length) ? data[index] : null; | ||||
|             //} | ||||
|             private readonly object[] data; | ||||
| 
 | ||||
|             private int length; | ||||
|             public int Length => length; | ||||
| 
 | ||||
|             internal Node(object[] data, int length) | ||||
|             { | ||||
|                 Helpers.DebugAssert((data == null && length == 0) || | ||||
|                     (data != null && length > 0 && length <= data.Length)); | ||||
|                 this.data = data; | ||||
| 
 | ||||
|                 this.length = length; | ||||
|             } | ||||
| 
 | ||||
|             public void RemoveLastWithMutate() | ||||
|             { | ||||
|                 if (length == 0) throw new InvalidOperationException(); | ||||
|                 length -= 1; | ||||
|             } | ||||
| 
 | ||||
|             public Node Append(object value) | ||||
|             { | ||||
|                 object[] newData; | ||||
|                 int newLength = length + 1; | ||||
|                 if (data == null) | ||||
|                 { | ||||
|                     newData = new object[10]; | ||||
|                 } | ||||
|                 else if (length == data.Length) | ||||
|                 { | ||||
|                     newData = new object[data.Length * 2]; | ||||
|                     Array.Copy(data, newData, length); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     newData = data; | ||||
|                 } | ||||
|                 newData[length] = value; | ||||
|                 return new Node(newData, newLength); | ||||
|             } | ||||
| 
 | ||||
|             public Node Trim() | ||||
|             { | ||||
|                 if (length == 0 || length == data.Length) return this; | ||||
|                 object[] newData = new object[length]; | ||||
|                 Array.Copy(data, newData, length); | ||||
|                 return new Node(newData, length); | ||||
|             } | ||||
| 
 | ||||
|             internal int IndexOfString(string value) | ||||
|             { | ||||
|                 for (int i = 0; i < length; i++) | ||||
|                 { | ||||
|                     if ((string)value == (string)data[i]) return i; | ||||
|                 } | ||||
|                 return -1; | ||||
|             } | ||||
| 
 | ||||
|             internal int IndexOfReference(object instance) | ||||
|             { | ||||
|                 for (int i = 0; i < length; i++) | ||||
|                 { | ||||
|                     if ((object)instance == (object)data[i]) return i; | ||||
|                 } // ^^^ (object) above should be preserved, even if this was typed; needs | ||||
|                   // to be a reference check | ||||
|                 return -1; | ||||
|             } | ||||
| 
 | ||||
|             internal int IndexOf(MatchPredicate predicate, object ctx) | ||||
|             { | ||||
|                 for (int i = 0; i < length; i++) | ||||
|                 { | ||||
|                     if (predicate(data[i], ctx)) return i; | ||||
|                 } | ||||
|                 return -1; | ||||
|             } | ||||
| 
 | ||||
|             internal void CopyTo(Array array, int offset) | ||||
|             { | ||||
|                 if (length > 0) | ||||
|                 { | ||||
|                     Array.Copy(data, 0, array, offset, length); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             internal void Clear() | ||||
|             { | ||||
|                 if (data != null) | ||||
|                 { | ||||
|                     Array.Clear(data, 0, data.Length); | ||||
|                 } | ||||
|                 length = 0; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal int IndexOf(MatchPredicate predicate, object ctx) | ||||
|         { | ||||
|             return head.IndexOf(predicate, ctx); | ||||
|         } | ||||
| 
 | ||||
|         internal int IndexOfString(string value) | ||||
|         { | ||||
|             return head.IndexOfString(value); | ||||
|         } | ||||
| 
 | ||||
|         internal int IndexOfReference(object instance) | ||||
|         { | ||||
|             return head.IndexOfReference(instance); | ||||
|         } | ||||
| 
 | ||||
|         internal delegate bool MatchPredicate(object value, object ctx); | ||||
| 
 | ||||
|         internal bool Contains(object value) | ||||
|         { | ||||
|             foreach (object obj in this) | ||||
|             { | ||||
|                 if (object.Equals(obj, value)) return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         internal sealed class Group | ||||
|         { | ||||
|             public readonly int First; | ||||
|             public readonly BasicList Items; | ||||
|             public Group(int first) | ||||
|             { | ||||
|                 this.First = first; | ||||
|                 this.Items = new BasicList(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal static BasicList GetContiguousGroups(int[] keys, object[] values) | ||||
|         { | ||||
|             if (keys == null) throw new ArgumentNullException(nameof(keys)); | ||||
|             if (values == null) throw new ArgumentNullException(nameof(values)); | ||||
|             if (values.Length < keys.Length) throw new ArgumentException("Not all keys are covered by values", nameof(values)); | ||||
|             BasicList outer = new BasicList(); | ||||
|             Group group = null; | ||||
|             for (int i = 0; i < keys.Length; i++) | ||||
|             { | ||||
|                 if (i == 0 || keys[i] != keys[i - 1]) { group = null; } | ||||
|                 if (group == null) | ||||
|                 { | ||||
|                     group = new Group(keys[i]); | ||||
|                     outer.Add(group); | ||||
|                 } | ||||
|                 group.Items.Add(values[i]); | ||||
|             } | ||||
|             return outer; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: be5fc2a1ac0731a44b0365987d942485 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,110 @@ | |||
| #if !NO_RUNTIME | ||||
| using System; | ||||
| using System.Reflection; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents the set of serialization callbacks to be used when serializing/deserializing a type. | ||||
|     /// </summary> | ||||
|     public class CallbackSet | ||||
|     { | ||||
|         private readonly MetaType metaType; | ||||
|         internal CallbackSet(MetaType metaType) | ||||
|         { | ||||
|             this.metaType = metaType ?? throw new ArgumentNullException(nameof(metaType)); | ||||
|         } | ||||
| 
 | ||||
|         internal MethodInfo this[TypeModel.CallbackType callbackType] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 switch (callbackType) | ||||
|                 { | ||||
|                     case TypeModel.CallbackType.BeforeSerialize: return beforeSerialize; | ||||
|                     case TypeModel.CallbackType.AfterSerialize: return afterSerialize; | ||||
|                     case TypeModel.CallbackType.BeforeDeserialize: return beforeDeserialize; | ||||
|                     case TypeModel.CallbackType.AfterDeserialize: return afterDeserialize; | ||||
|                     default: throw new ArgumentException("Callback type not supported: " + callbackType.ToString(), "callbackType"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal static bool CheckCallbackParameters(TypeModel model, MethodInfo method) | ||||
|         { | ||||
|             ParameterInfo[] args = method.GetParameters(); | ||||
|             for (int i = 0; i < args.Length; i++) | ||||
|             { | ||||
|                 Type paramType = args[i].ParameterType; | ||||
|                 if (paramType == model.MapType(typeof(SerializationContext))) { } | ||||
|                 else if (paramType == model.MapType(typeof(System.Type))) { } | ||||
| #if PLAT_BINARYFORMATTER | ||||
|                 else if (paramType == model.MapType(typeof(System.Runtime.Serialization.StreamingContext))) { } | ||||
| #endif | ||||
|                 else return false; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         private MethodInfo SanityCheckCallback(TypeModel model, MethodInfo callback) | ||||
|         { | ||||
|             metaType.ThrowIfFrozen(); | ||||
|             if (callback == null) return callback; // fine | ||||
|             if (callback.IsStatic) throw new ArgumentException("Callbacks cannot be static", nameof(callback)); | ||||
|             if (callback.ReturnType != model.MapType(typeof(void)) | ||||
|                 || !CheckCallbackParameters(model, callback)) | ||||
|             { | ||||
|                 throw CreateInvalidCallbackSignature(callback); | ||||
|             } | ||||
|             return callback; | ||||
|         } | ||||
| 
 | ||||
|         internal static Exception CreateInvalidCallbackSignature(MethodInfo method) | ||||
|         { | ||||
|             return new NotSupportedException("Invalid callback signature in " + method.DeclaringType.FullName + "." + method.Name); | ||||
|         } | ||||
| 
 | ||||
|         private MethodInfo beforeSerialize, afterSerialize, beforeDeserialize, afterDeserialize; | ||||
| 
 | ||||
|         /// <summary>Called before serializing an instance</summary> | ||||
|         public MethodInfo BeforeSerialize | ||||
|         { | ||||
|             get { return beforeSerialize; } | ||||
|             set { beforeSerialize = SanityCheckCallback(metaType.Model, value); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Called before deserializing an instance</summary> | ||||
|         public MethodInfo BeforeDeserialize | ||||
|         { | ||||
|             get { return beforeDeserialize; } | ||||
|             set { beforeDeserialize = SanityCheckCallback(metaType.Model, value); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Called after serializing an instance</summary> | ||||
|         public MethodInfo AfterSerialize | ||||
|         { | ||||
|             get { return afterSerialize; } | ||||
|             set { afterSerialize = SanityCheckCallback(metaType.Model, value); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary>Called after deserializing an instance</summary> | ||||
|         public MethodInfo AfterDeserialize | ||||
|         { | ||||
|             get { return afterDeserialize; } | ||||
|             set { afterDeserialize = SanityCheckCallback(metaType.Model, value); } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// True if any callback is set, else False | ||||
|         /// </summary> | ||||
|         public bool NonTrivial | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return beforeSerialize != null || beforeDeserialize != null | ||||
|                     || afterSerialize != null || afterDeserialize != null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: de0e7cb7bfcf4904aa31e910f241a8aa | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 170c607ac9d3b9346a8f4197e9e4d86a | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,17 @@ | |||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Indiate the variant of the protobuf .proto DSL syntax to use | ||||
|     /// </summary> | ||||
|     public enum ProtoSyntax | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// https://developers.google.com/protocol-buffers/docs/proto | ||||
|         /// </summary> | ||||
|         Proto2 = 0, | ||||
|         /// <summary> | ||||
|         /// https://developers.google.com/protocol-buffers/docs/proto3 | ||||
|         /// </summary> | ||||
|         Proto3 = 1, | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 8df2b30e0bc1f274a8170e86c9d08f96 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 0e4440bfa9e92f84d81d48e6c5b0022e | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,97 @@ | |||
| #if !NO_RUNTIME | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using ProtoBuf.Serializers; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Represents an inherited type in a type hierarchy. | ||||
|     /// </summary> | ||||
|     public sealed class SubType | ||||
|     { | ||||
|         internal sealed class Comparer : System.Collections.IComparer, IComparer<SubType> | ||||
|         { | ||||
|             public static readonly Comparer Default = new Comparer(); | ||||
| 
 | ||||
|             public int Compare(object x, object y) | ||||
|             { | ||||
|                 return Compare(x as SubType, y as SubType); | ||||
|             } | ||||
| 
 | ||||
|             public int Compare(SubType x, SubType y) | ||||
|             { | ||||
|                 if (ReferenceEquals(x, y)) return 0; | ||||
|                 if (x == null) return -1; | ||||
|                 if (y == null) return 1; | ||||
| 
 | ||||
|                 return x.FieldNumber.CompareTo(y.FieldNumber); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private int _fieldNumber; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The field-number that is used to encapsulate the data (as a nested | ||||
|         /// message) for the derived dype. | ||||
|         /// </summary> | ||||
|         public int FieldNumber | ||||
|         { | ||||
|             get => _fieldNumber; | ||||
|             internal set | ||||
|             { | ||||
|                 if (_fieldNumber != value) | ||||
|                 { | ||||
|                     MetaType.AssertValidFieldNumber(value); | ||||
|                     ThrowIfFrozen(); | ||||
|                     _fieldNumber = value; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private void ThrowIfFrozen() | ||||
|         { | ||||
|             if (serializer != null) throw new InvalidOperationException("The type cannot be changed once a serializer has been generated"); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The sub-type to be considered. | ||||
|         /// </summary> | ||||
|         public MetaType DerivedType => derivedType; | ||||
|         private readonly MetaType derivedType; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Creates a new SubType instance. | ||||
|         /// </summary> | ||||
|         /// <param name="fieldNumber">The field-number that is used to encapsulate the data (as a nested | ||||
|         /// message) for the derived dype.</param> | ||||
|         /// <param name="derivedType">The sub-type to be considered.</param> | ||||
|         /// <param name="format">Specific encoding style to use; in particular, Grouped can be used to avoid buffering, but is not the default.</param> | ||||
|         public SubType(int fieldNumber, MetaType derivedType, DataFormat format) | ||||
|         { | ||||
|             if (derivedType == null) throw new ArgumentNullException(nameof(derivedType)); | ||||
|             if (fieldNumber <= 0) throw new ArgumentOutOfRangeException(nameof(fieldNumber)); | ||||
|             _fieldNumber = fieldNumber; | ||||
|             this.derivedType = derivedType; | ||||
|             this.dataFormat = format; | ||||
|         } | ||||
| 
 | ||||
|         private readonly DataFormat dataFormat; | ||||
| 
 | ||||
|         private IProtoSerializer serializer; | ||||
| 
 | ||||
|         internal IProtoSerializer Serializer => serializer ?? (serializer = BuildSerializer()); | ||||
| 
 | ||||
|         private IProtoSerializer BuildSerializer() | ||||
|         { | ||||
|             // note the caller here is MetaType.BuildSerializer, which already has the sync-lock | ||||
|             WireType wireType = WireType.String; | ||||
|             if(dataFormat == DataFormat.Group) wireType = WireType.StartGroup; // only one exception | ||||
|              | ||||
|             IProtoSerializer ser = new SubItemSerializer(derivedType.Type, derivedType.GetKey(false, false), derivedType, false); | ||||
|             return new TagDecorator(_fieldNumber, wireType, false, ser); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: a2912d37917b74846bdcffe3daa174d2 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,33 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Event data associated with new types being added to a model | ||||
|     /// </summary> | ||||
|     public sealed class TypeAddedEventArgs : EventArgs | ||||
|     { | ||||
|         internal TypeAddedEventArgs(MetaType metaType) | ||||
|         { | ||||
|             MetaType = metaType; | ||||
|             ApplyDefaultBehaviour = true; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Whether or not to apply the default mapping behavior | ||||
|         /// </summary> | ||||
|         public bool ApplyDefaultBehaviour { get; set; } | ||||
|         /// <summary> | ||||
|         /// The configuration of the type being added | ||||
|         /// </summary> | ||||
|         public MetaType MetaType { get; } | ||||
|         /// <summary> | ||||
|         /// The type that was added to the model | ||||
|         /// </summary> | ||||
|         public Type Type => MetaType.Type; | ||||
|         /// <summary> | ||||
|         /// The model that is being changed | ||||
|         /// </summary> | ||||
|         public RuntimeTypeModel Model => MetaType.Model as RuntimeTypeModel; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 1500030a10d2168408f75fe907ce0568 | ||||
| MonoImporter: | ||||
|   externalObjects: {} | ||||
|   serializedVersion: 2 | ||||
|   defaultReferences: [] | ||||
|   executionOrder: 0 | ||||
|   icon: {instanceID: 0} | ||||
|   userData:  | ||||
|   assetBundleName:  | ||||
|   assetBundleVariant:  | ||||
|  | @ -0,0 +1,64 @@ | |||
| using System; | ||||
| 
 | ||||
| namespace ProtoBuf.Meta | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Event arguments needed to perform type-formatting functions; this could be resolving a Type to a string suitable for serialization, or could | ||||
|     /// be requesting a Type from a string. If no changes are made, a default implementation will be used (from the assembly-qualified names). | ||||
|     /// </summary> | ||||
|     public class TypeFormatEventArgs : EventArgs | ||||
|     { | ||||
|         private Type type; | ||||
|         private string formattedName; | ||||
|         private readonly bool typeFixed; | ||||
|         /// <summary> | ||||
|         /// The type involved in this map; if this is initially null, a Type is expected to be provided for the string in FormattedName. | ||||
|         /// </summary> | ||||
|         public Type Type | ||||
|         { | ||||
|             get { return type; } | ||||
|             set | ||||
|             { | ||||
|                 if (type != value) | ||||
|                 { | ||||
|                     if (typeFixed) throw new InvalidOperationException("The type is fixed and cannot be changed"); | ||||
|                     type = value; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The formatted-name involved in this map; if this is initially null, a formatted-name is expected from the type in Type. | ||||
|         /// </summary> | ||||
|         public string FormattedName | ||||
|         { | ||||
|             get { return formattedName; } | ||||
|             set | ||||
|             { | ||||
|                 if (formattedName != value) | ||||
|                 { | ||||
|                     if (!typeFixed) throw new InvalidOperationException("The formatted-name is fixed and cannot be changed"); | ||||
|                     formattedName = value; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         internal TypeFormatEventArgs(string formattedName) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(formattedName)) throw new ArgumentNullException("formattedName"); | ||||
|             this.formattedName = formattedName; | ||||
|             // typeFixed = false; <== implicit | ||||
|         } | ||||
| 
 | ||||
|         internal TypeFormatEventArgs(Type type) | ||||
|         { | ||||
|             this.type = type ?? throw new ArgumentNullException(nameof(type)); | ||||
|             typeFixed = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Delegate type used to perform type-formatting functions; the sender originates as the type-model. | ||||
|     /// </summary> | ||||
|     public delegate void TypeFormatEventHandler(object sender, TypeFormatEventArgs args); | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue