void GenerateReader(ProtoMessage m) { cw.Summary("Takes the remaining content of the json and deserialze it into the instance."); cw.Bracket("public static " + m.FullCsType + " Deserialize(SimpleJson.JsonObject json, " + m.FullCsType + " instance)"); cw.WriteLine(); //Prepare if field exist. cw.Comment("Assign Values"); foreach (Field f in m.Fields.Values) { cw.WriteLine("instance.Has" + f.CsName + " = json.ContainsKey(\"" + f.ProtoName + "\");"); } cw.WriteLine(); //Parse values foreach (Field f in m.Fields.Values) { string line = "if (instance.Has" + f.CsName + "){"; if (f.Rule == FieldRule.Repeated) { line += " SimpleJson.JsonArray jsonArray = json[\"" + f.ProtoName + "\"] as SimpleJson.JsonArray;"; line += " if (jsonArray != null){"; line += " foreach (var v in jsonArray){"; if (f.ProtoType is ProtoMessage) { line += " " + f.ProtoType.FullCsType + " ins = new " + f.ProtoType.FullCsType + "();"; line += " instance." + f.CsName + ".Add(" + f.ProtoType.FullCsType + ".Deserialize((SimpleJson.JsonObject)v, ins));"; } else { line += " instance." + f.CsName + ".Add(" + ConvertReader(f, "v") + ");"; } line += " }}"; } else if (f.ProtoType is ProtoMessage) { line += " instance." + f.CsName + " = new " + f.ProtoType.FullCsType + "();"; line += " " + f.ProtoType.FullCsType + ".Deserialize(json[\"" + f.ProtoName + "\"], instance." + f.CsName + ");"; } else { line += " instance." + f.CsName + " = " + ConvertReader(f, "json[\"" + f.ProtoName + "\"]") + ";"; } line += " }"; if (f.Rule == FieldRule.Required) { line += "else{"; line += " if (!json.ContainsKey(\"code\") || Convert.ToInt32(json[\"code\"]) == 0){"; line += " throw new ArgumentNullException(\"" + f.ProtoName + "\", \"Required by proto specification.\");"; line += " }}"; } cw.WriteLine(line); } cw.WriteLine(); cw.WriteLine("return instance;"); cw.EndBracket(); }
public void GenerateClass(ProtoMessage m) { var baseclass = m.BaseClass ?? ""; if (baseclass.Length > 0) { baseclass = " : " + baseclass; } //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType + baseclass); if (options.GenerateDefaultConstructors) { GenerateCtorForDefaults(m); } GenerateEnums(m); GenerateProperties(m); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub); cw.WriteLine(); } cw.EndBracket(); return; }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> void GenerateProperties(ProtoMessage m) { foreach (Field f in m.Fields.Values) { if (f.Comments != null) { cw.Summary(f.Comments); } cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } //Add HasField foreach (Field f in m.Fields.Values) { cw.WriteLine("public bool Has" + f.CsName + " { get; set; }"); cw.WriteLine(); } //Wire format field ID #if DEBUGx cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> static void GenerateProperties(ProtoMessage m, CodeWriter cw) { foreach (Field f in m.Fields.Values) { if (f.OptionExternal) { cw.WriteLine("//" + GenerateProperty(f) + " // Implemented by user elsewhere"); } else { if (f.Comments != null) { cw.Summary(f.Comments); } cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } } //Wire format field ID #if DEBUG cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }
/// <summary> /// Generates inline writer of a length delimited byte array /// </summary> static void BytesWriter(string stream, string memoryStream, CodeWriter cw) { cw.Comment("Length delimited byte array"); //Original //cw.WriteLine("ProtocolParser.WriteBytes(" + stream + ", " + memoryStream + ".ToArray());"); //Much slower than original /* * cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); * cw.WriteLine(memoryStream + ".Seek(0, System.IO.SeekOrigin.Begin);"); * cw.WriteLine(memoryStream + ".CopyTo(" + stream + ");"); */ //Same speed as original /* * cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); * cw.WriteLine(stream + ".Write(" + memoryStream + ".ToArray(), 0, (int)" + memoryStream + ".Length);"); */ //10% faster than original using GetBuffer rather than ToArray cw.WriteLine("int " + memoryStream + "Length = " + memoryStream + ".Length();"); cw.WriteLine("ProtocolParser.WriteUInt32_(" + stream + ", " + memoryStream + "Length);"); cw.WriteLine(stream + ".Write(" + memoryStream + ".GetBuffer(), 0, " + memoryStream + "Length);"); }
/// <summary> /// Generates inline writer of a length delimited byte array /// </summary> static void BytesWriter(Field f, string stream, CodeWriter cw) { cw.Comment("Length delimited byte array"); //Original //cw.WriteLine("ProtocolParser.WriteBytes(" + stream + ", " + memoryStream + ".ToArray());"); //Much slower than original /* * cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); * cw.WriteLine(memoryStream + ".Seek(0, System.IO.SeekOrigin.Begin);"); * cw.WriteLine(memoryStream + ".CopyTo(" + stream + ");"); */ //Same speed as original /* * cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); * cw.WriteLine(stream + ".Write(" + memoryStream + ".ToArray(), 0, (int)" + memoryStream + ".Length);"); */ //10% faster than original using GetBuffer rather than ToArray cw.WriteLine("uint length" + f.ID + " = (uint)msField.Length;"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(" + stream + ", length" + f.ID + ");"); cw.WriteLine(stream + ".Write(msField.GetBuffer(), 0, (int)length" + f.ID + ");"); }
static void KeyWriter(string stream, int id, Wire wire, CodeWriter cw) { uint n = ((uint)id << 3) | ((uint)wire); cw.Comment("Key for field: " + id + ", " + wire); //cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", " + n + ");"); VarintWriter(stream, n, cw); }
public static void GenerateClass(ProtoMessage m, CodeWriter cw, Options options) { //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType + " : IDisposable, Pool.IPooled, IProto"); GenerateEnums(m, cw); GenerateProperties(m, cw, options); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw, options); cw.WriteLine(); } cw.EndBracket(); return; }
public static void GenerateClass(ProtoMessage m, CodeWriter cw, Options options) { //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType); GenerateEnums(m, cw); GenerateProperties(m, cw); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw, options); cw.WriteLine(); } cw.EndBracket(); return; }
public static void GenerateClass(ProtoMessage m, CodeWriter cw) { //Do not generate class code for external classes /*if (m.OptionExternal) * { * cw.Comment("Written elsewhere"); * cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType + " {}"); * return; * }*/ //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType); GenerateEnums(m, cw); GenerateProperties(m, cw); if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw); cw.WriteLine(); } cw.EndBracket(); return; }
public static void GenerateClass(ProtoMessage m, CodeWriter cw) { //Do not generate class code for external classes /*if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType + " {}"); return; }*/ //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType); GenerateEnums(m, cw); GenerateProperties(m, cw); if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw); cw.WriteLine(); } cw.EndBracket(); return; }
/// <summary> /// Return true for normal code and false if generated thrown exception. /// In the latter case a break is not needed to be generated afterwards. /// </summary> public bool FieldReader(Field f) { if (f.Rule == FieldRule.Repeated) { //Make sure we are not reading a list of interfaces cw.Comment("repeated"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "stream", "br", null) + ");"); } else { if (f.ProtoType is ProtoMessage) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", null) + ";"); cw.WriteLine("else"); cw.WriteIndent(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return(true); } cw.WriteLine("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); } return(true); }
/// <summary> /// Generates inline writer of a length delimited byte array /// </summary> static void BytesWriter(string stream, string memoryStream, CodeWriter cw) { cw.Comment("Length delimited byte array"); //Original //cw.WriteLine("ProtocolParser.WriteBytes(" + stream + ", " + memoryStream + ".ToArray());"); //Much slower than original /* cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); cw.WriteLine(memoryStream + ".Seek(0, System.IO.SeekOrigin.Begin);"); cw.WriteLine(memoryStream + ".CopyTo(" + stream + ");"); */ //Same speed as original /* cw.WriteLine("ProtocolParser.WriteUInt32(" + stream + ", (uint)" + memoryStream + ".Length);"); cw.WriteLine(stream + ".Write(" + memoryStream + ".ToArray(), 0, (int)" + memoryStream + ".Length);"); */ //10% faster than original using GetBuffer rather than ToArray cw.WriteLine("uint " + memoryStream + "Length = (uint)" + memoryStream + ".Length;"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(" + stream + ", " + memoryStream + "Length);"); cw.WriteLine(stream + ".Write(" + memoryStream + ".GetBuffer(), 0, (int)" + memoryStream + "Length);"); }
/// <summary> /// Return true for normal code and false for generated throw. /// In the later case a break is not needed to be generated afterwards. /// </summary> public static bool FieldReader(Field f, CodeWriter cw) { if (f.Rule == FieldRule.Repeated) { //Make sure we are not reading a list of interfaces if (f.ProtoType.OptionType == "interface") { cw.WriteLine("throw new InvalidOperationException(\"Can't deserialize a list of interfaces\");"); return false; } if (f.OptionPacked == true) { //TODO: read without buffering cw.Comment("repeated packed"); cw.Using("var ms" + f.ID + " = new MemoryStream(global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadBytes(stream))"); if (f.IsUsingBinaryWriter) cw.WriteLine("BinaryReader br" + f.ID + " = new BinaryReader(ms" + f.ID + ");"); cw.WhileBracket("ms" + f.ID + ".Position < ms" + f.ID + ".Length"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "ms" + f.ID, "br" + f.ID, null) + ");"); cw.EndBracket(); cw.EndBracket(); } else { cw.Comment("repeated"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "stream", "br", null) + ");"); } } else { if (f.OptionReadOnly) { //The only "readonly" fields we can modify //We could possibly support bytes primitive too but it would require the incoming length to match the wire length if (f.ProtoType is ProtoMessage) { cw.WriteLine(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return true; } cw.WriteLine("throw new InvalidOperationException(\"Can't deserialize into a readonly primitive field\");"); return false; } if (f.ProtoType is ProtoMessage) { if (f.ProtoType.OptionType == "struct") { cw.WriteLine(FieldReaderType(f, "stream", "br", "ref instance." + f.CsName) + ";"); return true; } cw.WriteLine("if (instance." + f.CsName + " == null)"); if (f.ProtoType.OptionType == "interface") cw.WriteIndent("throw new InvalidOperationException(\"Can't deserialize into a interfaces null pointer\");"); else cw.WriteIndent("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", null) + ";"); cw.WriteLine("else"); cw.WriteIndent(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return true; } cw.WriteLine("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); } return true; }
static void GenerateReader(ProtoMessage m, CodeWriter cw) { #region Helper Deserialize Methods string refstr = (m.OptionType == "struct") ? "ref " : ""; if (m.OptionType != "interface") { cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(Stream stream)"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("Deserialize(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLengthDelimited(Stream stream)"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("DeserializeLengthDelimited(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLength(Stream stream, int length)"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("DeserializeLength(stream, length, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: put the buffer into a MemoryStream and create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(byte[] buffer)"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("using (var ms = new MemoryStream(buffer))"); cw.WriteIndent("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); } cw.Summary("Helper: put the buffer into a MemoryStream before deserializing"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(byte[] buffer, " + refstr + m.FullCsType + " instance)"); cw.WriteLine("using (var ms = new MemoryStream(buffer))"); cw.WriteIndent("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); #endregion string[] methods = new string[] { "Deserialize", //Default old one "DeserializeLengthDelimited", //Start by reading length prefix and stay within that limit "DeserializeLength", //Read at most length bytes given by argument }; //Main Deserialize foreach (string method in methods) { if (method == "Deserialize") { cw.Summary("Takes the remaining content of the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLengthDelimited") { cw.Summary("Read the VarInt length prefix and the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLength") { cw.Summary("Read the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, int length, " + refstr + m.FullCsType + " instance)"); } else { throw new NotImplementedException(); } if (m.IsUsingBinaryWriter) { cw.WriteLine("BinaryReader br = new BinaryReader(stream);"); } //Prepare List<> and default values foreach (Field f in m.Fields.Values) { if (f.Rule == FieldRule.Repeated) { if (f.OptionReadOnly == false) { //Initialize lists of the custom DateTime or TimeSpan type. string csType = f.ProtoType.FullCsType; if (f.OptionCodeType != null) { csType = f.OptionCodeType; } cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("instance." + f.CsName + " = new List<" + csType + ">();"); } } else if (f.OptionDefault != null) { cw.WriteLine("instance." + f.CsName + " = " + f.FormatForTypeAssignment() + ";"); } else if (f.Rule == FieldRule.Optional) { if (f.ProtoType is ProtoEnum) { ProtoEnum pe = f.ProtoType as ProtoEnum; //the default value is the first value listed in the enum's type definition foreach (var kvp in pe.Enums) { cw.WriteLine("instance." + f.CsName + " = " + pe.FullCsType + "." + kvp.Name + ";"); break; } } } } if (method == "DeserializeLengthDelimited") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt32(stream);"); cw.WriteLine("limit += stream.Position;"); } if (method == "DeserializeLength") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = stream.Position + length;"); } cw.WhileBracket("true"); if (method == "DeserializeLengthDelimited" || method == "DeserializeLength") { cw.IfBracket("stream.Position >= limit"); cw.WriteLine("if (stream.Position == limit)"); cw.WriteIndent("break;"); cw.WriteLine("else"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Read past max limit\");"); cw.EndBracket(); } cw.WriteLine("int keyByte = stream.ReadByte();"); cw.WriteLine("if (keyByte == -1)"); if (method == "Deserialize") { cw.WriteIndent("break;"); } else { cw.WriteIndent("throw new System.IO.EndOfStreamException();"); } //Determine if we need the lowID optimization bool hasLowID = false; foreach (Field f in m.Fields.Values) { if (f.ID < 16) { hasLowID = true; break; } } if (hasLowID) { cw.Comment("Optimized reading of known fields with field ID < 16"); cw.Switch("keyByte"); foreach (Field f in m.Fields.Values) { if (f.ID >= 16) { continue; } cw.Dedent(); cw.Comment("Field " + f.ID + " " + f.WireType); cw.Indent(); cw.Case(((f.ID << 3) | (int)f.WireType)); if (FieldSerializer.FieldReader(f, cw)) { cw.WriteLine("continue;"); } } cw.SwitchEnd(); cw.WriteLine(); } cw.WriteLine("var key = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadKey((byte)keyByte, stream);"); cw.WriteLine(); cw.Comment("Reading field ID > 16 and unknown field ID/wire type combinations"); cw.Switch("key.Field"); cw.Case(0); cw.WriteLine("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Invalid field id: 0, something went wrong in the stream\");"); foreach (Field f in m.Fields.Values) { if (f.ID < 16) { continue; } cw.Case(f.ID); //Makes sure we got the right wire type cw.WriteLine("if(key.WireType != global::SilentOrbit.ProtocolBuffers.Wire." + f.WireType + ")"); cw.WriteIndent("break;"); //This can be changed to throw an exception for unknown formats. if (FieldSerializer.FieldReader(f, cw)) { cw.WriteLine("continue;"); } } cw.CaseDefault(); if (m.OptionPreserveUnknown) { cw.WriteLine("if (instance.PreservedFields == null)"); cw.WriteIndent("instance.PreservedFields = new List<global::SilentOrbit.ProtocolBuffers.KeyValue>();"); cw.WriteLine("instance.PreservedFields.Add(new global::SilentOrbit.ProtocolBuffers.KeyValue(key, global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadValueBytes(stream, key)));"); } else { cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.SkipKey(stream, key);"); } cw.WriteLine("break;"); cw.SwitchEnd(); cw.EndBracket(); cw.WriteLine(); if (m.OptionTriggers) { cw.WriteLine("instance.AfterDeserialize();"); } cw.WriteLine("return instance;"); cw.EndBracket(); cw.WriteLine(); } return; }
/// <summary> /// Generate code for reading and writing protocol buffer messages /// </summary> public static void Save(ProtoCollection file, string csPath) { string ext = Path.GetExtension(csPath); string prefix = csPath.Substring(0, csPath.Length - ext.Length); string csDir = Path.GetDirectoryName(csPath); if (Directory.Exists(csDir) == false) { Directory.CreateDirectory(csDir); } //Basic structures using (var cw = new CodeWriter(csPath)) { cw.Comment(@"Classes and structures being serialized"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run. To make custom modifications, edit the .proto file and add //:external before the message line then write the code and the changes in a separate file."); //cw.WriteLine("using System;"); //cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); //string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) {/* * if (ns != m.CsNamespace) * { * if (ns != null) //First time * cw.EndBracket(); * cw.Bracket("namespace " + m.CsNamespace); * ns = m.CsNamespace; * }*/ MessageCode.GenerateClass(m, cw); cw.WriteLine(); } foreach (ProtoEnum e in file.Enums.Values) {/* * if (ns != e.CsNamespace) * { * if (ns != null) //First time * cw.EndBracket(); * cw.Bracket("namespace " + e.CsNamespace); * ns = e.CsNamespace; * }*/ MessageCode.GenerateEnum(e, cw); cw.WriteLine(); } //cw.EndBracket(); } //.Serializer.cs //Code for Reading/Writing using (var cw = new CodeWriter(prefix + ".Serializer" + ext)) { cw.Comment(@"This is the backend code for reading and writing"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run."); //cw.WriteLine("using System;"); //cw.WriteLine("using System.IO;"); cw.WriteLine(); //string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { //if (ns != m.CsNamespace) //{ // if (ns != null) //First time // cw.EndBracket(); // cw.Bracket("namespace " + m.CsNamespace); // ns = m.CsNamespace; //} MessageSerializer.GenerateClassSerializer(m, cw); } //cw.EndBracket(); } string libPath = Path.Combine(Path.GetDirectoryName(csPath), "ProtocolParser.cs"); using (TextWriter codeWriter = new StreamWriter(libPath, false, Encoding.UTF8)) { ReadCode(codeWriter, "ProtocolParser", true); ReadCode(codeWriter, "ProtocolParserFixed", false); ReadCode(codeWriter, "ProtocolParserKey", false); ReadCode(codeWriter, "ProtocolParserVarInt", false); } string output1 = File.ReadAllText(libPath); string output2 = File.ReadAllText(csPath); string output3 = File.ReadAllText(prefix + ".Serializer" + ext); File.WriteAllText(prefix + ".Serializer" + ".ci.cs", output1 + output2 + output3); }
/// <summary> /// Return true for normal code and false if generated thrown exception. /// In the latter case a break is not needed to be generated afterwards. /// </summary> public bool FieldReader(Field f) { if (f.Rule == FieldRule.Repeated) { //Make sure we are not reading a list of interfaces if (f.ProtoType.OptionType == "interface") { cw.WriteLine("throw new NotSupportedException(\"Can't deserialize a list of interfaces\");"); return(false); } if (f.OptionPacked == true) { cw.Comment("repeated packed"); cw.WriteLine("long end" + f.ID + " = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt32(stream);"); cw.WriteLine("end" + f.ID + " += stream.Position;"); cw.WhileBracket("stream.Position < end" + f.ID); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "stream", "br", null) + ");"); cw.EndBracket(); cw.WriteLine("if (stream.Position != end" + f.ID + ")"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Read too many bytes in packed data\");"); } else { cw.Comment("repeated"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "stream", "br", null) + ");"); } } else { if (f.OptionReadOnly) { //The only "readonly" fields we can modify //We could possibly support bytes primitive too but it would require the incoming length to match the wire length if (f.ProtoType is ProtoMessage) { cw.WriteLine(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return(true); } cw.WriteLine("throw new InvalidOperationException(\"Can't deserialize into a readonly primitive field\");"); return(false); } if (f.ProtoType is ProtoMessage) { if (f.ProtoType.OptionType == "struct") { if (options.Nullable) { cw.WriteLine("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", null) + ";"); } else { cw.WriteLine(FieldReaderType(f, "stream", "br", "ref instance." + f.CsName) + ";"); } return(true); } cw.WriteLine("if (instance." + f.CsName + " == null)"); if (f.ProtoType.OptionType == "interface") { cw.WriteIndent("throw new InvalidOperationException(\"Can't deserialize into a interfaces null pointer\");"); } else { cw.WriteIndent("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", null) + ";"); } cw.WriteLine("else"); cw.WriteIndent(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return(true); } cw.WriteLine("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); } return(true); }
public void GenerateClass(ProtoMessage m) { if (options.NoGenerateImported && m.IsImported) { Console.Error.WriteLine("Skipping imported " + m.FullProtoName); return; } //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType + " : ProtoBuf.IExtensible, ProtoBuf.IMessage"); cw.WriteLine("global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing){ return null; }"); cw.WriteLine("public byte[] SerializeToBytes(){return SerializeToBytes(this);}"); cw.WriteLine("public void SerializeLengthDelimited(Stream stream){SerializeLengthDelimited(stream, this);}"); cw.WriteLine(@"public struct stMessageOperator : ProtoBuf.IMessageOperator { public ProtoBuf.IMessage New() { return new MessageClass(); } public ProtoBuf.IMessage Deserialize(Stream stream) { return MessageClass.Deserialize(stream); } public ProtoBuf.IMessage DeserializeLengthDelimited(Stream stream) { return MessageClass.DeserializeLengthDelimited(stream); } public ProtoBuf.IMessage DeserializeLength(Stream stream, int length) { return MessageClass.DeserializeLength(stream, length); } public ProtoBuf.IMessage Deserialize(byte[] buffer) { return MessageClass.Deserialize(buffer); } public ProtoBuf.IMessage Deserialize(byte[] buffer, ProtoBuf.IMessage instance) { return MessageClass.Deserialize(buffer, (MessageClass)instance); } public ProtoBuf.IMessage Deserialize(Stream stream, ProtoBuf.IMessage instance) { return MessageClass.Deserialize(stream, (MessageClass)instance); } public ProtoBuf.IMessage DeserializeLengthDelimited(Stream stream, ProtoBuf.IMessage instance) { return MessageClass.DeserializeLengthDelimited(stream, (MessageClass)instance); } public ProtoBuf.IMessage DeserializeLength(Stream stream, int length, ProtoBuf.IMessage instance) { return MessageClass.DeserializeLength(stream, length, (MessageClass)instance); } public void Serialize(Stream stream, ProtoBuf.IMessage instance) { MessageClass.Serialize(stream, (MessageClass)instance); } public byte[] SerializeToBytes(ProtoBuf.IMessage instance) { return MessageClass.SerializeToBytes((MessageClass)instance); } public void SerializeLengthDelimited(Stream stream, ProtoBuf.IMessage instance) { MessageClass.SerializeLengthDelimited(stream, (MessageClass)instance); } } public static ProtoBuf.IMessageOperator MessageOperator = new stMessageOperator(); ".Replace("MessageClass", m.CsType)); if (options.GenerateDefaultConstructors) { GenerateCtorForDefaults(m); } GenerateEnums(m); GenerateProperties(m); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub); cw.WriteLine(); } cw.EndBracket(); return; }
static void GenerateReader(ProtoMessage m, CodeWriter cw) { #region Helper Deserialize Methods string refstr = (m.OptionType == "struct") ? "ref " : ""; if (m.OptionType != "interface") {/* cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(CitoStream stream)"); cw.WriteLine(m.FullCsType + " instance = new " + m.FullCsType + "();"); cw.WriteLine("Deserialize(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); */ cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " DeserializeLengthDelimitedNew(CitoStream stream)"); cw.WriteLine(m.FullCsType + " instance = new " + m.FullCsType + "();"); cw.WriteLine("DeserializeLengthDelimited(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); /* cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " DeserializeLength(CitoStream stream, int length)"); cw.WriteLine(m.FullCsType + " instance = new " + m.FullCsType + "();"); cw.WriteLine("DeserializeLength(stream, length, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: put the buffer into a MemoryStream and create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(byte[] buffer, int length)"); cw.WriteLine(m.FullCsType + " instance = new " + m.FullCsType + "();"); cw.WriteLine("CitoMemoryStream ms = CitoMemoryStream.Create(buffer, length);"); cw.WriteIndent("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace();*/ } cw.Summary("Helper: put the buffer into a MemoryStream before deserializing"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " DeserializeBuffer(byte[] buffer, int length, " + refstr + m.FullCsType + " instance)"); cw.WriteLine("CitoMemoryStream ms = CitoMemoryStream.Create(buffer, length);"); cw.WriteIndent("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); #endregion string[] methods = new string[] { "Deserialize", //Default old one "DeserializeLengthDelimited", //Start by reading length prefix and stay within that limit "DeserializeLength", //Read at most length bytes given by argument }; //Main Deserialize foreach (string method in methods) { if (method == "Deserialize") { cw.Summary("Takes the remaining content of the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(CitoStream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLengthDelimited") { cw.Summary("Read the VarInt length prefix and the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(CitoStream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLength") { cw.Summary("Read the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(CitoStream stream, int length, " + refstr + m.FullCsType + " instance)"); } else throw new NotImplementedException(); if (m.IsUsingBinaryWriter) cw.WriteLine("BinaryReader br = new BinaryReader(stream);"); //Prepare List<> and default values foreach (Field f in m.Fields.Values) { if (f.Rule == FieldRule.Repeated) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteLine("{"); cw.WriteIndent("instance." + f.CsName + " = new " + f.ProtoType.FullCsType + "[1];"); cw.WriteIndent("instance." + f.CsName + "Count = 0;"); cw.WriteIndent("instance." + f.CsName + "Length = 1;"); cw.WriteLine("}"); } else if (f.OptionDefault != null) { if (f.ProtoType is ProtoEnum) cw.WriteLine("instance." + f.CsName + " = " + f.ProtoType.FullCsType + "Enum." + f.OptionDefault + ";"); else cw.WriteLine("instance." + f.CsName + " = " + f.OptionDefault + ";"); } else if (f.Rule == FieldRule.Optional) { if (f.ProtoType is ProtoEnum) { ProtoEnum pe = f.ProtoType as ProtoEnum; //the default value is the first value listed in the enum's type definition foreach (var kvp in pe.Enums) { cw.WriteLine("instance." + f.CsName + " = " + pe.FullCsType + "_" + kvp.Name + ";"); break; } } } } if (method == "DeserializeLengthDelimited") { //Important to read stream position after we have read the length field cw.WriteLine("int limit = ProtocolParser.ReadUInt32(stream);"); cw.WriteLine("limit += stream.Position();"); } if (method == "DeserializeLength") { //Important to read stream position after we have read the length field cw.WriteLine("int limit = stream.Position() + length;"); } cw.WhileBracket("true"); if (method == "DeserializeLengthDelimited" || method == "DeserializeLength") { cw.IfBracket("stream.Position() >= limit"); cw.WriteLine("if(stream.Position() == limit)"); cw.WriteIndent("break;"); cw.WriteLine("else"); cw.WriteIndent("//throw new InvalidOperationException(\"Read past max limit\");"); cw.WriteIndent("return null;"); cw.EndBracket(); } cw.WriteLine("int keyByte = stream.ReadByte();"); cw.WriteLine("if (keyByte == -1)"); if (method == "Deserialize") cw.WriteIndent("break;"); else { cw.WriteIndent("//throw new System.IO.EndOfStreamException();"); cw.WriteIndent("return null;"); } //Determine if we need the lowID optimization bool hasLowID = false; foreach (Field f in m.Fields.Values) { if (f.ID < 16) { hasLowID = true; break; } } if (hasLowID) { cw.Comment("Optimized reading of known fields with field ID < 16"); cw.Switch("keyByte"); foreach (Field f in m.Fields.Values) { if (f.ID >= 16) continue; cw.Comment("Field " + f.ID + " " + f.WireType); cw.Case(((f.ID << 3) | (int)f.WireType)); if (FieldSerializer.FieldReader(f, cw)) cw.WriteLine("continue;"); } cw.WriteLine("default: break;"); cw.EndBracket(); cw.WriteLine(); } cw.WriteLine("#if CITO\n Key key = ProtocolParser.ReadKey_(keyByte.LowByte, stream);"); cw.WriteLine("#else\n Key key = ProtocolParser.ReadKey_((byte)keyByte, stream);\n#endif"); cw.WriteLine(); cw.Comment("Reading field ID > 16 and unknown field ID/wire type combinations"); cw.Switch("key.GetField()"); cw.Case(0); cw.WriteLine("//throw new InvalidDataException(\"Invalid field id: 0, something went wrong in the stream\");"); cw.WriteLine("return null;"); foreach (Field f in m.Fields.Values) { if (f.ID < 16) continue; cw.Case(f.ID); //Makes sure we got the right wire type cw.WriteLine("if(key.GetWireType() != Wire." + f.WireType + ")"); cw.WriteIndent("break;"); //This can be changed to throw an exception for unknown formats. if (FieldSerializer.FieldReader(f, cw)) cw.WriteLine("continue;"); } cw.CaseDefault(); if (m.OptionPreserveUnknown) { cw.WriteLine("if (instance.PreservedFields == null)"); cw.WriteIndent("instance.PreservedFields = new List<KeyValue>();"); cw.WriteLine("instance.PreservedFields.Add(new KeyValue(key, ProtocolParser.ReadValueBytes(stream, key)));"); } else { cw.WriteLine("ProtocolParser.SkipKey(stream, key);"); } cw.WriteLine("break;"); cw.EndBracket(); cw.EndBracket(); cw.WriteLine(); if (m.OptionTriggers) cw.WriteLine("instance.AfterDeserialize();"); cw.WriteLine("return instance;"); cw.EndBracket(); cw.WriteLine(); } return; }
/// <summary> /// Generate code for reading and writing protocol buffer messages /// </summary> public static void Save(ProtoCollection file, string csPath) { string ext = Path.GetExtension(csPath); string prefix = csPath.Substring(0, csPath.Length - ext.Length); string csDir = Path.GetDirectoryName(csPath); if (Directory.Exists(csDir) == false) Directory.CreateDirectory(csDir); //Basic structures using (var cw = new CodeWriter(csPath)) { cw.Comment(@"Classes and structures being serialized"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run. To make custom modifications, edit the .proto file and add //:external before the message line then write the code and the changes in a separate file."); cw.WriteLine("using System;"); cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time cw.EndBracket(); cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } MessageCode.GenerateClass(m, cw); cw.WriteLine(); } foreach (ProtoEnum e in file.Enums.Values) { if (ns != e.CsNamespace) { if (ns != null) //First time cw.EndBracket(); cw.Bracket("namespace " + e.CsNamespace); ns = e.CsNamespace; } MessageCode.GenerateEnum(e, cw); cw.WriteLine(); } cw.EndBracket(); } //.Serializer.cs //Code for Reading/Writing using (var cw = new CodeWriter(prefix + ".Serializer" + ext)) { cw.Comment(@"This is the backend code for reading and writing"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run."); cw.WriteLine("using System;"); cw.WriteLine("using System.IO;"); cw.WriteLine("using System.Text;"); cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time cw.EndBracket(); cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } MessageSerializer.GenerateClassSerializer(m, cw); } cw.EndBracket(); } string libPath = Path.Combine(Path.GetDirectoryName(csPath), "ProtocolParser.cs"); using (TextWriter codeWriter = new StreamWriter(libPath, false, Encoding.UTF8)) { codeWriter.NewLine = "\r\n"; ReadCode(codeWriter, "ProtocolParser", true); ReadCode(codeWriter, "ProtocolParserFixed", false); ReadCode(codeWriter, "ProtocolParserKey", false); ReadCode(codeWriter, "ProtocolParserVarInt", false); } }
static void GeneratePooled(ProtoMessage m, CodeWriter cw, Options options) { cw.WriteLine("#region [Methods] Pooled"); cw.WriteLine("private bool _disposed;"); cw.WriteLine("public bool ShouldPool = true;"); cw.WriteLine(); #region [Method] Dispose cw.Bracket("public virtual void Dispose()"); cw.WriteLine("if (this._disposed)"); cw.WriteIndent("return;"); cw.WriteLine("this.ResetToPool();"); cw.WriteLine("this._disposed = true;"); cw.EndBracketSpace(); #endregion #region [Method] ResetToPool cw.Bracket("public void ResetToPool()"); cw.WriteLine("ResetToPool(this);"); cw.EndBracketSpace(); #endregion #region [Method] [Static] ResetToPool cw.Bracket($"public static void ResetToPool({m.CsType} instance)"); cw.WriteLine("if (!instance.ShouldPool)"); cw.WriteIndent("return;"); string GetFieldType(Field field) { string csType = field.ProtoType.FullCsType; if (field.OptionCodeType != null) { csType = field.OptionCodeType; } return(csType); } // Generate Default Structs List <string> defStructs = new List <string>(); foreach (var field in m.Fields.Values) { string csFullType = GetFieldType(field); string csType = field.ProtoType.CsType; string fName = field.CsName; cw.Comment($"[{csType}] {fName}"); // Generate Default Structs if (field.Rule != FieldRule.Repeated && field.ProtoType.OptionType == "struct" && string.IsNullOrEmpty(field.OptionDefault) && defStructs.Contains(csFullType) == false) { cw.WriteLine($"{csFullType} def{csType} = new {csFullType}();"); defStructs.Add(csFullType); } if (field.Rule == FieldRule.Repeated) { cw.Bracket($"if (instance.{field.CsName} != null)"); if (field.ProtoType is ProtoMessage) { cw.ForBracket($"int i = 0; i < instance.{fName}.Count; i++"); cw.Bracket($"if (instance.{fName}[i] != null)"); cw.WriteLine($"instance.{fName}[i].ResetToPool();"); cw.WriteLine($"instance.{fName}[i] = null;"); cw.EndBracket(); cw.EndBracket(); } cw.WriteLine($"List<{csFullType}> ins{fName} = instance.{fName};"); cw.WriteLine($"Pool.FreeList<{csFullType}>(ref ins{fName});"); cw.WriteLine($"instance.{fName} = ins{fName};"); cw.EndBracket(); } else { if (field.ProtoType is ProtoMessage && field.ProtoType.OptionType != "struct") { cw.Bracket($"if (instance.{fName} != null)"); cw.WriteLine($"instance.{fName}.ResetToPool();"); cw.WriteLine($"instance.{fName} = null;"); cw.EndBracket(); } else { if (string.IsNullOrEmpty(field.OptionDefault) == false) { cw.WriteLine($"instance.{fName} = {field.FormatForTypeAssignment()};"); } else if (field.ProtoType.OptionType == "struct") { cw.WriteLine($"instance.{fName} = def{csType};"); } else { cw.WriteLine($"instance.{fName} = default({csFullType});"); } } } cw.WriteLine(); } defStructs.Clear(); cw.WriteLine($"Pool.Free<{m.FullCsType}>(ref instance);"); cw.EndBracketSpace(); #endregion #region [Method] EnterPool cw.Bracket("public virtual void EnterPool()"); cw.WriteLine("this._disposed = true;"); cw.EndBracketSpace(); #endregion #region [Method] LeavePool cw.Bracket("public virtual void LeavePool()"); cw.WriteLine("this._disposed = false;"); cw.EndBracketSpace(); #endregion cw.WriteLine("#endregion"); }
static void GenerateReader(ProtoMessage m, CodeWriter cw, Options options) { cw.WriteLine("#region [Methods] Reader"); #region [Method] ReadFromStream if (m.OptionExternal == false) { cw.Bracket("public void ReadFromStream(Stream stream, int size)"); { cw.WriteLine("DeserializeLength(stream, size, this);"); } cw.EndBracketSpace(); } #endregion #region Helper Deserialize Methods string refstr = (m.OptionType == "struct") ? "ref " : ""; if (m.OptionType != "interface") { var newInstance = m.OptionType == "struct" ? $"new {m.CsType}();" : $"Pool.Get<{m.CsType}>();"; cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(Stream stream)"); { cw.WriteLine(m.CsType + " instance = " + newInstance); cw.WriteLine("Deserialize(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); } cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLengthDelimited(Stream stream)"); { cw.WriteLine(m.CsType + " instance = " + newInstance); cw.WriteLine("DeserializeLengthDelimited(stream, " + refstr + "instance);"); cw.WriteLine("return instance;"); } cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLength(Stream stream, int length)"); { cw.WriteLine(m.CsType + " instance = " + newInstance); cw.WriteLine("DeserializeLength(stream, length, " + refstr + "instance);"); cw.WriteLine("return instance;"); } cw.EndBracketSpace(); cw.Summary("Helper: put the buffer into a MemoryStream and create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(byte[] buffer)"); { cw.WriteLine(m.CsType + " instance = " + newInstance); cw.WriteLine("var ms = Pool.Get<MemoryStream>();"); cw.WriteLine("ms.Write(buffer, 0 ,buffer.Length);"); cw.WriteLine("ms.Position = 0;"); cw.WriteLine("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("Pool.FreeMemoryStream(ref ms);"); cw.WriteLine("return instance;"); } cw.EndBracketSpace(); } cw.Summary("Helper: put the buffer into a MemoryStream before deserializing"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(byte[] buffer, " + refstr + m.FullCsType + " instance)"); cw.WriteLine("var ms = Pool.Get<MemoryStream>();"); cw.WriteLine("ms.Write(buffer, 0 ,buffer.Length);"); cw.WriteLine("ms.Position = 0;"); cw.WriteLine("Deserialize(ms, " + refstr + "instance);"); cw.WriteLine("Pool.FreeMemoryStream(ref ms);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); #endregion string[] methods = new string[] { "Deserialize", //Default old one "DeserializeLengthDelimited", //Start by reading length prefix and stay within that limit "DeserializeLength", //Read at most length bytes given by argument }; //Main Deserialize foreach (string method in methods) { if (method == "Deserialize") { cw.Summary("Takes the remaining content of the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLengthDelimited") { cw.Summary("Read the VarInt length prefix and the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLength") { cw.Summary("Read the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream, int length, " + refstr + m.FullCsType + " instance)"); } else { throw new NotImplementedException(); } GenerateDefaults(m, cw, options); if (method == "DeserializeLengthDelimited") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt32(stream);"); cw.WriteLine("limit += stream.Position;"); } if (method == "DeserializeLength") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = stream.Position + length;"); } cw.WhileBracket("true"); if (method == "DeserializeLengthDelimited" || method == "DeserializeLength") { cw.IfBracket("stream.Position >= limit"); cw.WriteLine("if (stream.Position == limit)"); cw.WriteIndent("break;"); cw.WriteLine("else"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Read past max limit\");"); cw.EndBracket(); } cw.WriteLine("int keyByte = stream.ReadByte();"); cw.WriteLine("if (keyByte == -1)"); if (method == "Deserialize") { cw.WriteIndent("break;"); } else { cw.WriteIndent("throw new System.IO.EndOfStreamException();"); } //Determine if we need the lowID optimization bool hasLowID = false; foreach (Field f in m.Fields.Values) { if (f.ID < 16) { hasLowID = true; break; } } if (hasLowID) { cw.Comment("Optimized reading of known fields with field ID < 16"); cw.Switch("keyByte"); foreach (Field f in m.Fields.Values) { if (f.ID >= 16) { continue; } cw.Dedent(); cw.Comment("Field " + f.ID + " " + f.WireType); cw.Indent(); cw.Case(((f.ID << 3) | (int)f.WireType)); if (FieldSerializer.FieldReader(f, cw)) { cw.WriteLine("continue;"); } } cw.SwitchEnd(); cw.WriteLine(); } cw.WriteLine("var key = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadKey((byte)keyByte, stream);"); cw.WriteLine(); cw.Comment("Reading field ID > 16 and unknown field ID/wire type combinations"); cw.Switch("key.Field"); cw.Case(0); cw.WriteLine("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Invalid field id: 0, something went wrong in the stream\");"); foreach (Field f in m.Fields.Values) { if (f.ID < 16) { continue; } cw.Case(f.ID); //Makes sure we got the right wire type cw.WriteLine("if(key.WireType != global::SilentOrbit.ProtocolBuffers.Wire." + f.WireType + ")"); cw.WriteIndent("break;"); //This can be changed to throw an exception for unknown formats. if (FieldSerializer.FieldReader(f, cw)) { cw.WriteLine("continue;"); } } cw.CaseDefault(); if (m.OptionPreserveUnknown) { cw.WriteLine("if (instance.PreservedFields == null)"); cw.WriteIndent("instance.PreservedFields = new List<global::SilentOrbit.ProtocolBuffers.KeyValue>();"); cw.WriteLine("instance.PreservedFields.Add(new global::SilentOrbit.ProtocolBuffers.KeyValue(key, global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadValueBytes(stream, key)));"); } else { cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.SkipKey(stream, key);"); } cw.WriteLine("break;"); cw.SwitchEnd(); cw.EndBracket(); cw.WriteLine(); if (m.OptionTriggers) { cw.WriteLine("instance.AfterDeserialize();"); } cw.WriteLine("return instance;"); cw.EndBracket(); cw.WriteLine(); } cw.WriteLine("#endregion"); return; }
/// <summary> /// Generate code for reading and writing protocol buffer messages /// </summary> public static void Save(ProtoCollection file, Options options) { string csPath = options.OutputPath; CodeWriter.DefaultIndentPrefix = options.UseTabs ? "\t" : " "; string ext = Path.GetExtension(csPath); string prefix = csPath.Substring(0, csPath.Length - ext.Length); string csDir = Path.GetDirectoryName(csPath); if (Directory.Exists(csDir) == false) { Directory.CreateDirectory(csDir); } //Basic structures using (var cw = new CodeWriter(csPath)) { cw.Comment(@"Classes and structures being serialized"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run. To make custom modifications, edit the .proto file and add //:external before the message line then write the code and the changes in a separate file."); cw.WriteLine("using System;"); cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); var messageCode = new MessageCode(cw, options); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } if (m.CsNamespace == "global") { ns = null; } else { cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } } messageCode.GenerateClass(m); cw.WriteLine(); } foreach (ProtoEnum e in file.Enums.Values) { if (ns != e.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } cw.Bracket("namespace " + e.CsNamespace); ns = e.CsNamespace; } messageCode.GenerateEnum(e); cw.WriteLine(); } if (ns != null) { cw.EndBracket(); } } //.Serializer.cs //Code for Reading/Writing using (var cw = new CodeWriter(prefix + ".Serializer" + ext)) { cw.Comment(@"This is the backend code for reading and writing"); cw.WriteLine(); cw.Comment(@"Generated by ProtocolBuffer - a pure c# code generation implementation of protocol buffers Report bugs to: https://silentorbit.com/protobuf/"); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run."); cw.WriteLine("using System;"); cw.WriteLine("using System.IO;"); cw.WriteLine("using System.Text;"); cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); cw.WriteLine("#pragma warning disable 0472, 0162"); cw.WriteLine(); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } if (m.CsNamespace == "global") { ns = null; } else { cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } } var messageSerializer = new MessageSerializer(cw, options); messageSerializer.GenerateClassSerializer(m); } if (ns != null) { cw.EndBracket(); } } string libPath = Path.Combine(Path.GetDirectoryName(csPath), "ProtocolParser.cs"); using (TextWriter codeWriter = new StreamWriter(libPath, false, Encoding.UTF8)) { codeWriter.NewLine = "\r\n"; ReadCode(codeWriter, "MessageTable", true); ReadCode(codeWriter, "ProtocolParser", false); ReadCode(codeWriter, "ProtocolParserExceptions", false); ReadCode(codeWriter, "ProtocolParserFixed", false); ReadCode(codeWriter, "ProtocolParserKey", false); ReadCode(codeWriter, "ProtocolParserMemory", false); ReadCode(codeWriter, "ProtocolParserVarInt", false); } }
public void GenerateClass(ProtoMessage m) { if (options.NoGenerateImported && m.IsImported) { Console.Error.WriteLine("Skipping imported " + m.FullProtoName); return; } //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); if (options.GenerateForProtoDotNet) { cw.WriteLine("[ProtoContract]"); cw.Bracket(m.OptionAccess + " " + m.OptionType + " " + m.CsType); } else { cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType); } if (options.GenerateDefaultConstructors) { GenerateCtorForDefaults(m); } GenerateEnums(m); GenerateProperties(m); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub); cw.WriteLine(); } cw.EndBracket(); return; }
void GenerateReader(ProtoMessage m) { string mTableParamDefs, mTableParams; FindMessageTableParams(m, out mTableParamDefs, out mTableParams); #region Helper Deserialize Methods string refstr = (m.OptionType == "struct") ? "ref " : ""; if (m.OptionType != "interface") { if (!m.OptionNoInstancing) { cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(Stream stream" + mTableParamDefs + ")"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("Deserialize(stream" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLengthDelimited(Stream stream" + mTableParamDefs + ")"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("DeserializeLengthDelimited(stream" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " DeserializeLength(Stream stream" + mTableParamDefs + ", int length)"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("DeserializeLength(stream" + mTableParams + ", length, " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: put the buffer into a MemoryStream and create a new instance to deserializing into"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(byte[] buffer" + mTableParamDefs + ")"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("using (var ms = new MemoryStream(buffer))"); cw.WriteIndent("Deserialize(ms" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance when deserializing a JObject"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(global::Newtonsoft.Json.Linq.JObject obj" + mTableParamDefs + ")"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("Deserialize(obj" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); cw.Summary("Helper: create a new instance and deserialize JSON from a string"); cw.Bracket(m.OptionAccess + " static " + m.CsType + " Deserialize(string json" + mTableParamDefs + ")"); cw.WriteLine(m.CsType + " instance = new " + m.CsType + "();"); cw.WriteLine("Deserialize(global::Newtonsoft.Json.Linq.JObject.Parse(json)" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); } if (!m.OptionNoPartials) { cw.Summary("Load this value from a proto buffer"); cw.Bracket(m.OptionAccess + " void FromProto(Stream stream" + mTableParamDefs + ")"); cw.WriteLine("Deserialize(stream" + mTableParams + ", this );"); cw.EndBracketSpace(); cw.Summary("Load this value from a json object"); cw.Bracket(m.OptionAccess + " void FromJson(global::Newtonsoft.Json.Linq.JObject obj" + mTableParamDefs + ")"); cw.WriteLine("Deserialize(obj" + mTableParams + ", this );"); cw.EndBracketSpace(); } } cw.Summary("Helper: put the buffer into a MemoryStream before deserializing"); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(byte[] buffer" + mTableParamDefs + ", " + refstr + m.FullCsType + " instance)"); cw.WriteLine("using (var ms = new MemoryStream(buffer))"); cw.WriteIndent("Deserialize(ms" + mTableParams + ", " + refstr + "instance);"); cw.WriteLine("return instance;"); cw.EndBracketSpace(); #endregion string[] methods = new string[] { "Deserialize", //Default old one "DeserializeLengthDelimited", //Start by reading length prefix and stay within that limit "DeserializeLength", //Read at most length bytes given by argument }; //Main Deserialize foreach (string method in methods) { if (method == "Deserialize") { cw.Summary("Takes the remaining content of the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream" + mTableParamDefs + ", " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLengthDelimited") { cw.Summary("Read the VarInt length prefix and the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream" + mTableParamDefs + ", " + refstr + m.FullCsType + " instance)"); } else if (method == "DeserializeLength") { cw.Summary("Read the given number of bytes from the stream and deserialze it into the instance."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " " + method + "(Stream stream" + mTableParamDefs + ", int length, " + refstr + m.FullCsType + " instance)"); } else { throw new NotImplementedException(); } if (m.IsUsingBinaryWriter) { cw.WriteLine("BinaryReader br = new BinaryReader(stream);"); } GenerateDefaults(m); if (method == "DeserializeLengthDelimited") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt32(stream);"); cw.WriteLine("limit += stream.Position;"); } if (method == "DeserializeLength") { //Important to read stream position after we have read the length field cw.WriteLine("long limit = stream.Position + length;"); } cw.WhileBracket("true"); if (method == "DeserializeLengthDelimited" || method == "DeserializeLength") { cw.IfBracket("stream.Position >= limit"); cw.WriteLine("if (stream.Position == limit)"); cw.WriteIndent("break;"); cw.WriteLine("else"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Read past max limit\");"); cw.EndBracket(); } cw.WriteLine("int keyByte = stream.ReadByte();"); cw.WriteLine("if (keyByte == -1)"); if (method == "Deserialize") { cw.WriteIndent("break;"); } else { cw.WriteIndent("throw new System.IO.EndOfStreamException();"); } //Determine if we need the lowID optimization bool hasLowID = false; foreach (Field f in m.Fields.Values) { if (f.ID < 16) { hasLowID = true; break; } } if (hasLowID) { cw.Comment("Optimized reading of known fields with field ID < 16"); cw.Switch("keyByte"); foreach (Field f in m.Fields.Values) { if (f.ID >= 16) { continue; } cw.Dedent(); cw.Comment("Field " + f.ID + " " + f.WireType); cw.Indent(); cw.Case(((f.ID << 3) | (int)f.WireType)); if (fieldSerializer.FieldReader(f, m.RequiredMessageTables)) { cw.WriteLine("continue;"); } } cw.SwitchEnd(); cw.WriteLine(); } cw.WriteLine("var key = global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadKey((byte)keyByte, stream);"); cw.WriteLine(); cw.Comment("Reading field ID > 16 and unknown field ID/wire type combinations"); cw.Switch("key.Field"); cw.Case(0); cw.WriteLine("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"Invalid field id: 0, something went wrong in the stream\");"); foreach (Field f in m.Fields.Values) { if (f.ID < 16) { continue; } cw.Case(f.ID); //Makes sure we got the right wire type cw.WriteLine("if(key.WireType != global::SilentOrbit.ProtocolBuffers.Wire." + f.WireType + ")"); cw.WriteIndent("break;"); //This can be changed to throw an exception for unknown formats. if (fieldSerializer.FieldReader(f, m.RequiredMessageTables)) { cw.WriteLine("continue;"); } } cw.CaseDefault(); if (m.OptionPreserveUnknown) { cw.WriteLine("if (instance.PreservedFields == null)"); cw.WriteIndent("instance.PreservedFields = new List<global::SilentOrbit.ProtocolBuffers.KeyValue>();"); cw.WriteLine("instance.PreservedFields.Add(new global::SilentOrbit.ProtocolBuffers.KeyValue(key, global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadValueBytes(stream, key)));"); } else { cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.SkipKey(stream, key);"); } cw.WriteLine("break;"); cw.SwitchEnd(); cw.EndBracket(); cw.WriteLine(); if (m.OptionTriggers) { cw.WriteLine("instance.AfterDeserialize();"); } cw.WriteLine("return instance;"); cw.EndBracket(); cw.WriteLine(); } //JSON deserialize cw.Summary("Deserializes an instance from a JSON object."); cw.Bracket(m.OptionAccess + " static " + m.FullCsType + " Deserialize(global::Newtonsoft.Json.Linq.JObject obj" + mTableParamDefs + ", " + refstr + m.FullCsType + " instance)"); GenerateDefaults(m); cw.WriteLine(); cw.Bracket("foreach (var property in obj.Properties())"); cw.Switch("property.Name"); foreach (var f in m.Fields.Values) { cw.Case("\"" + f.CsName + "\""); fieldSerializer.JsonFieldReader(f, m.RequiredMessageTables, "property.Value"); cw.WriteLine("break;"); } cw.SwitchEnd(); cw.EndBracket(); cw.WriteLine(); if (m.OptionTriggers) { cw.WriteLine("instance.AfterDeserialize();"); } cw.WriteLine("return instance;"); cw.EndBracket(); cw.WriteLine(); return; }
/// <summary> /// Return true for normal code and false for generated throw. /// In the later case a break is not needed to be generated afterwards. /// </summary> public static bool FieldReader(Field f, CodeWriter cw) { if (f.Rule == FieldRule.Repeated) { //Make sure we are not reading a list of interfaces if (f.ProtoType.OptionType == "interface") { cw.WriteLine("throw new InvalidOperationException(\"Can't deserialize a list of interfaces\");"); return(false); } if (f.OptionPacked == true) { //TODO: read without buffering cw.Comment("repeated packed"); cw.Using("var ms" + f.ID + " = new MemoryStream(global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadBytes(stream))"); if (f.IsUsingBinaryWriter) { cw.WriteLine("BinaryReader br" + f.ID + " = new BinaryReader(ms" + f.ID + ");"); } cw.WhileBracket("ms" + f.ID + ".Position < ms" + f.ID + ".Length"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "ms" + f.ID, "br" + f.ID, null) + ");"); cw.EndBracket(); cw.EndBracket(); } else { cw.Comment("repeated"); cw.WriteLine("instance." + f.CsName + ".Add(" + FieldReaderType(f, "stream", "br", null) + ");"); } } else { if (f.OptionReadOnly) { //The only "readonly" fields we can modify //We could possibly support bytes primitive too but it would require the incoming length to match the wire length if (f.ProtoType is ProtoMessage) { cw.WriteLine(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return(true); } cw.WriteLine("throw new InvalidOperationException(\"Can't deserialize into a readonly primitive field\");"); return(false); } if (f.ProtoType is ProtoMessage) { if (f.ProtoType.OptionType == "struct") { cw.WriteLine(FieldReaderType(f, "stream", "br", "ref instance." + f.CsName) + ";"); return(true); } cw.WriteLine("if (instance." + f.CsName + " == null)"); if (f.ProtoType.OptionType == "interface") { cw.WriteIndent("throw new InvalidOperationException(\"Can't deserialize into a interfaces null pointer\");"); } else { cw.WriteIndent("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", null) + ";"); } cw.WriteLine("else"); cw.WriteIndent(FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); return(true); } cw.WriteLine("instance." + f.CsName + " = " + FieldReaderType(f, "stream", "br", "instance." + f.CsName) + ";"); } return(true); }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> static void GenerateProperties(ProtoMessage m, CodeWriter cw) { foreach (Field f in m.Fields.Values) { if (f.OptionExternal) cw.WriteLine("//" + GenerateProperty(f) + " // Implemented by user elsewhere"); else { if (f.Comments != null) cw.Summary(f.Comments); cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } } //Wire format field ID #if DEBUG cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> static void GenerateProperties(ProtoMessage m, CodeWriter cw) { foreach (Field f in m.Fields.Values) { if (f.OptionExternal) cw.WriteLine("//" + GenerateProperty(f) + " // Implemented by user elsewhere"); else { if (f.Comments != null) cw.Summary(f.Comments); cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } } // Implement ToString cw.Bracket("public override string ToString()"); string returnStatement = "return \"\""; Dictionary<int, Field>.ValueCollection fields = m.Fields.Values; if (fields.Count > 0) { List<string> fieldElements = new List<string>(); foreach (Field f in fields) { string fieldHeaderCode = "\"" + f.CsName + ": \" + "; string fieldToStringCode; string fieldCommaCode = " + \", \""; if (f.Rule == FieldRule.Optional && f.ProtoType.Nullable) { // Hide optional nullable fields: this makes logging cleaner for union types fieldToStringCode = string.Format("({0} != null ? {1}{0}{2}: \"\")", f.CsName, fieldHeaderCode, fieldCommaCode); } else if (f.Rule == FieldRule.Repeated && f.ProtoTypeName == "bytes") { // Always output repeated fields with [] fieldToStringCode = string.Format("{1}\"[\" + ({0} != null ? string.Join(\", \", {0}.ConvertAll<string>(o => o != null ? BitConverter.ToString(o) : \"\").ToArray()) : \"\") + \"]\"{2}", f.CsName, fieldHeaderCode, fieldCommaCode); } else if (f.Rule == FieldRule.Repeated) { // Always output repeated fields with [] fieldToStringCode = string.Format("{1}\"[\" + ({0} != null ? string.Join(\", \", {0}.ConvertAll<string>(o => o.ToString()).ToArray()) : \"\") + \"]\"{2}", f.CsName, fieldHeaderCode, fieldCommaCode); } else if (f.ProtoTypeName == "bytes") { // Special code to output bytes fieldToStringCode = string.Format("{1}\"[\" + ({0} != null ? BitConverter.ToString({0}) : \"\") + \"]\"{2}", f.CsName, fieldHeaderCode, fieldCommaCode); } else { fieldToStringCode = fieldHeaderCode + f.CsName + fieldCommaCode; } fieldElements.Add(fieldToStringCode); } returnStatement = "return " + string.Join(" + \n", fieldElements) + ";"; } cw.WriteLine(returnStatement); cw.EndBracket(); //Wire format field ID #if DEBUG cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }
/// <summary> /// Generate code for reading and writing protocol buffer messages /// </summary> public static void Save(ProtoCollection file, Options options) { string csPath = options.OutputPath; CodeWriter.DefaultIndentPrefix = options.UseTabs ? "\t" : " "; string ext = Path.GetExtension(csPath); string prefix = csPath.Substring(0, csPath.Length - ext.Length); string csDir = Path.GetDirectoryName(csPath); if (Directory.Exists(csDir) == false) { Directory.CreateDirectory(csDir); } //Basic structures using (var cw = new CodeWriter(csPath)) { if (string.IsNullOrWhiteSpace(file.Comments) == false) { cw.Comment(file.Comments); cw.WriteLine(); } cw.Comment(@"Classes and structures being serialized"); cw.WriteLine(); cw.Comment(Disclamer); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run. To make custom modifications, edit the .proto file and add //:external before the message line then write the code and the changes in a separate file."); cw.WriteLine(); cw.Comment("ReSharper disable InconsistentNaming"); cw.WriteLine(); cw.WriteLine("using System;"); cw.WriteLine("using System.Collections.Generic;"); if (options.DataContractAttributes) { cw.WriteLine("using System.Runtime.Serialization;"); } cw.WriteLine(); var messageCode = new MessageCode(cw, options); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } messageCode.GenerateClass(m); cw.WriteLine(); } foreach (ProtoEnum e in file.Enums.Values) { if (ns != e.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } cw.Bracket("namespace " + e.CsNamespace); ns = e.CsNamespace; } messageCode.GenerateEnum(e); cw.WriteLine(); } if (ns != null) { cw.EndBracket(); } } //.Serializer.cs //Code for Reading/Writing using (var cw = new CodeWriter(prefix + ".Serializer" + ext)) { if (string.IsNullOrWhiteSpace(file.Comments) == false) { cw.Comment(file.Comments); cw.WriteLine(); } cw.Comment(@"This is the backend code for reading and writing"); cw.WriteLine(); cw.Comment(Disclamer); cw.WriteLine(); cw.Comment(@"DO NOT EDIT This file will be overwritten when CodeGenerator is run."); cw.WriteLine(); cw.Comment("ReSharper disable InconsistentNaming"); cw.WriteLine(); cw.WriteLine("using System;"); cw.WriteLine("using System.IO;"); cw.WriteLine("using System.Text;"); cw.WriteLine("using System.Collections.Generic;"); cw.WriteLine(); string ns = null; //avoid writing namespace between classes if they belong to the same foreach (ProtoMessage m in file.Messages.Values) { if (ns != m.CsNamespace) { if (ns != null) //First time { cw.EndBracket(); } cw.Bracket("namespace " + m.CsNamespace); ns = m.CsNamespace; } var messageSerializer = new MessageSerializer(cw, options); messageSerializer.GenerateClassSerializer(m); } if (ns != null) { cw.EndBracket(); } } //Option to no include ProtocolParser.cs in the output directory if (options.NoProtocolParser) { Console.Error.WriteLine("ProtocolParser.cs not (re)generated"); } else { string libPath = Path.Combine(Path.GetDirectoryName(csPath), "ProtocolParser.cs"); using (TextWriter codeWriter = new StreamWriter(libPath, false, Encoding.UTF8)) { codeWriter.NewLine = "\r\n"; ReadCode(codeWriter, "ProtocolParser", true); ReadCode(codeWriter, "ProtocolParserExceptions", false); ReadCode(codeWriter, "ProtocolParserFixed", false); ReadCode(codeWriter, "ProtocolParserKey", false); ReadCode(codeWriter, "ProtocolParserMemory", false); if (options.Net2 == false) { ReadCode(codeWriter, "ProtocolParserMemory4", false); } ReadCode(codeWriter, "ProtocolParserVarInt", false); } } }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> static void GenerateProperties(ProtoMessage m, CodeWriter cw) { foreach (Field f in m.Fields.Values) { if (f.OptionExternal) { cw.WriteLine("//" + GenerateProperty(f) + " // Implemented by user elsewhere"); } else { if (f.Comments != null) { cw.Summary(f.Comments); } cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } } // Implement ToString cw.Bracket("public override string ToString()"); string returnStatement = "return \"\""; Dictionary <int, Field> .ValueCollection fields = m.Fields.Values; if (fields.Count > 0) { List <string> fieldElements = new List <string>(); foreach (Field f in fields) { string fieldHeaderCode = "\"" + f.CsName + ": \" + "; string fieldToStringCode; string fieldCommaCode = " + \", \""; if (f.Rule == FieldRule.Optional && f.ProtoType.Nullable) { // Hide optional nullable fields: this makes logging cleaner for union types fieldToStringCode = string.Format("({0} != null ? {1}{0}{2}: \"\")", f.CsName, fieldHeaderCode, fieldCommaCode); } else if (f.Rule == FieldRule.Repeated) { // Always output repeated fields with [] fieldToStringCode = string.Format("{1}\"[\" + ({0} != null ? string.Join(\", \", {0}.ConvertAll<string>(o => o.ToString()).ToArray()) : \"\") + \"]\"{2}", f.CsName, fieldHeaderCode, fieldCommaCode); } else if (f.ProtoTypeName == "bytes") { // Special code to output bytes fieldToStringCode = string.Format("{1}\"[\" + ({0} != null ? BitConverter.ToString({0}) : \"\") + \"]\"{2}", f.CsName, fieldHeaderCode, fieldCommaCode); } else { fieldToStringCode = fieldHeaderCode + f.CsName + fieldCommaCode; } fieldElements.Add(fieldToStringCode); } returnStatement = "return " + string.Join(" + \n", fieldElements) + ";"; } cw.WriteLine(returnStatement); cw.EndBracket(); //Wire format field ID #if DEBUG cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }