using System; using System.Collections.Generic; namespace ProtoCSStruct { public class MessageWriter { public void Write(ProtoMessage message, FileWriter fw) { string unsafestr = ""; if(message.Unsafe) { unsafestr = "unsafe "; } fw.Write(unsafestr + "public struct " + message.Name + " : IStructMessage<" + message.Name + ">"); fw.Write("{"); fw.Write(" public static StructMessageParser<" + message.Name + "> Parser = new StructMessageParser<" + message.Name + ">();"); fw.Write(" public const int SizeOf = " + message.GetMessageSize() + ";"); fw.Indent(); foreach (var field in message.Fields) { if(field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteRepeatedFiled(field, fw); } else { WriteFiled(field, fw); } } //结构体内存占用大小,就是SizeOf WriteFun_StructSizeof(message, fw); //结构体名字 WriteFun_GetName(message, fw); //结构体实际打包后的大小,字节 WriteFun_CalculateSize(message, fw); //是否为空的意思是所有字段都是缺省值,int等是0,bool是false,enum是0,string是"" //WriteFun_IsEmpty(message, fw); //清空所有字段 WriteFun_Clear(message, fw); //从另一个结构体复制数据,提供这个提高copy效率,直接用结构体复制就是memcpy,对大结构体来说效率不高,repeated字段满的情况毕竟少见 WriteFun_CopyFrom(message, fw); //序列化 WriteFun_WriteTo(message, fw); //反序列化 WriteFun_ReadFrom(message, fw); //打印消息,类json(不完全是)格式 WriteFun_ToString(message, fw); fw.Outdent(); fw.Write("}"); //struct fw.Write(""); } private void WriteFiled(ProtoMessageField field , FileWriter fw) { string cstypename = field.GetFiledTypeNameInCSharp(); string fielddef = string.Format("public {0} {1};", cstypename, field.Name); fw.Write(fielddef); } private void WriteRepeatedFiled(ProtoMessageField field, FileWriter fw) { string cstypename = field.RepeatedFiledTypeNameInCSharp; string fielddef = string.Format("public {0} {1};", cstypename, field.Name); fw.Write(fielddef); } # region WriteFun Struct private void WriteFun_StructSizeof(ProtoMessage message, FileWriter fw) { fw.Write(""); fw.Write( "public int StructSizeof()", "{", " return SizeOf;", "}" ); fw.Write(""); } private void WriteFun_GetName(ProtoMessage message, FileWriter fw) { fw.Write(""); fw.Write( "public string GetName()", "{", " return \"" + message.Name + "\";", "}" ); fw.Write(""); } private void WriteFun_CalculateSize(ProtoMessage message, FileWriter fw) { fw.Write( "public int CalculateSize()", "{", " int size = 0;" ); fw.Indent(); foreach (var field in message.Fields) { if (field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_CalculateSize_RepeatedFiled(message, field, fw); } else { WriteFun_CalculateSize_Filed(message, field, fw); } } fw.Write("return size;"); fw.Outdent(); fw.Write("}"); fw.Write(""); } private void WriteFun_CalculateSize_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { string checkfieldempty = "if (" + field.Name; string computesizefield = ""; if (field.FieldType == FieldType.Int32) { checkfieldempty += " != 0)"; computesizefield = "WireFormat.ComputeInt32Size(" + field.Name + ");"; } if (field.FieldType == FieldType.UInt32) { checkfieldempty += " != 0)"; computesizefield = "WireFormat.ComputeUInt32Size(" + field.Name + ");"; } if (field.FieldType == FieldType.Int64) { checkfieldempty += " != 0)"; computesizefield = "WireFormat.ComputeInt64Size(" + field.Name + ");"; } if (field.FieldType == FieldType.UInt64) { checkfieldempty += " != 0)"; computesizefield = "WireFormat.ComputeUInt64Size(" + field.Name + ");"; } if (field.FieldType == FieldType.Bool) { checkfieldempty += " != false)"; computesizefield = "WireFormat.ComputeBoolSize(" + field.Name + ");"; } if (field.FieldType == FieldType.Enum) { checkfieldempty += " != 0)"; computesizefield = "WireFormat.ComputeEnumSize((int)" + field.Name + ");"; } uint tag = WireFormat.MakeTag(field.Number, field.FieldType); uint tagsize = (uint)WireFormat.ComputeTagSize(tag); if (field.FieldType == FieldType.String) { checkfieldempty += ".Length != 0)"; fw.Write( checkfieldempty, "{", " int length = " + field.Name + ".CalculateSize();", " size += " + tagsize + " + length + WireFormat.ComputeLengthSize(length);", "}" ); } else if (field.FieldType == FieldType.Message) { checkfieldempty += ".CalculateSize() != 0)"; fw.Write( checkfieldempty, "{", " int length = " + field.Name + ".CalculateSize();", " size += " + tagsize + " + length + WireFormat.ComputeLengthSize(length);", "}" ); } else { fw.Write( checkfieldempty, "{", " size += " + tagsize + " + " + computesizefield, "}" ); } } private void WriteFun_CalculateSize_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { uint tag = WireFormat.MakeTag(field.Number, WireFormat.WireType.LengthDelimited); string checkfieldempty = "if (" + field.Name; string computesizefield = ""; checkfieldempty += ".GetCount() != 0)"; computesizefield = field.Name + ".CalculateSize(" + tag + ");"; fw.Write( checkfieldempty, "{", " size += " + computesizefield, "}" ); } private void WriteFun_IsEmpty(ProtoMessage message, FileWriter fw) { fw.Write( "public bool IsEmpty()", "{", " return CalculateSize() == 0;", "}" ); fw.Write(""); } private void WriteFun_Clear(ProtoMessage message, FileWriter fw) { fw.Write( "public void Clear()", "{" ); fw.Indent(); foreach (var field in message.Fields) { if (field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_Clear_RepeatedFiled(message, field, fw); } else { WriteFun_Clear_Filed(message, field, fw); } } fw.Write(""); fw.Outdent(); fw.Write("}"); fw.Write(""); } private void WriteFun_Clear_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { string checkfieldempty = "if (" + field.Name; string clearfield = ""; if (field.FieldType == FieldType.Int32) { checkfieldempty += " != 0)"; clearfield = field.Name + " = 0;"; } if (field.FieldType == FieldType.UInt32) { checkfieldempty += " != 0)"; clearfield = field.Name + " = 0;"; } if (field.FieldType == FieldType.Int64) { checkfieldempty += " != 0)"; clearfield = field.Name + " = 0;"; } if (field.FieldType == FieldType.UInt64) { checkfieldempty += " != 0)"; clearfield = field.Name + " = 0;"; } if (field.FieldType == FieldType.Bool) { checkfieldempty += " != false)"; clearfield = field.Name + " = false;"; } if (field.FieldType == FieldType.Enum) { checkfieldempty += " != 0)"; clearfield = field.Name + " = 0;"; } if (field.FieldType == FieldType.Message || field.FieldType == FieldType.String) { clearfield = field.Name + ".Clear();"; fw.Write( clearfield ); } else { fw.Write( checkfieldempty, "{", " " + clearfield, "}" ); } } private void WriteFun_Clear_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { fw.Write( field.Name + ".Clear();" ); } private void WriteFun_CopyFrom(ProtoMessage message, FileWriter fw) { fw.Write( "public void CopyFrom(ref " + message.Name + " other)", "{" ); fw.Indent(); foreach (var field in message.Fields) { if (field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_CopyFrom_RepeatedFiled(message, field, fw); } else { WriteFun_CopyFrom_Filed(message, field, fw); } } fw.Write(""); fw.Outdent(); fw.Write("}"); fw.Write(""); } private void WriteFun_CopyFrom_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { if (field.FieldType == FieldType.Int32 || field.FieldType == FieldType.UInt32 || field.FieldType == FieldType.Int64 || field.FieldType == FieldType.UInt64 || field.FieldType == FieldType.Bool || field.FieldType == FieldType.Enum) { fw.Write( field.Name + " = other." + field.Name + ";" ); } if(field.FieldType == FieldType.String || field.FieldType == FieldType.Message) { fw.Write( field.Name + ".CopyFrom(ref other." + field.Name + ");" ); } } private void WriteFun_CopyFrom_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { fw.Write( field.Name + ".CopyFrom(ref other." + field.Name + ");" ); } private void WriteFun_WriteTo(ProtoMessage message, FileWriter fw) { fw.Write( "public void WriteTo(CodedOutputStream output)", "{" ); fw.Indent(); foreach (var field in message.Fields) { if(field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_WriteTo_RepeatedFiled(message, field, fw); } else { WriteFun_WriteTo_Filed(message, field, fw); } } fw.Outdent(); fw.Write("}"); fw.Write(""); } private void WriteFun_WriteTo_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { string checkfieldempty = "if (" + field.Name; string writefield = ""; if (field.FieldType == FieldType.Int32) { checkfieldempty += " != 0)"; writefield = "output.WriteInt32(" + field.Name + ");"; } if (field.FieldType == FieldType.UInt32) { checkfieldempty += " != 0)"; writefield = "output.WriteUInt32(" + field.Name + ");"; } if (field.FieldType == FieldType.Int64) { checkfieldempty += " != 0)"; writefield = "output.WriteInt64(" + field.Name + ");"; } if (field.FieldType == FieldType.UInt64) { checkfieldempty += " != 0)"; writefield = "output.WriteUInt64(" + field.Name + ");"; } if (field.FieldType == FieldType.Bool) { checkfieldempty += " != false)"; writefield = "output.WriteBool(" + field.Name + ");"; } if (field.FieldType == FieldType.Enum) { checkfieldempty += " != 0)"; writefield = "output.WriteEnum((int)" + field.Name + ");"; } if (field.FieldType == FieldType.String) { checkfieldempty += ".Length != 0)"; writefield = field.Name + ".WriteTo(output);"; } uint tag = WireFormat.MakeTag(field.Number, field.FieldType); if(field.FieldType == FieldType.Message) { checkfieldempty += ".CalculateSize() != 0)"; writefield = field.Name + ".WriteTo(output);"; fw.Write( checkfieldempty, "{", " output.WriteTag(" + tag + ");", " int length = " + field.Name + ".CalculateSize();", " output.WriteLength(length);", " " + writefield, "}" ); } else { fw.Write( checkfieldempty, "{", " output.WriteTag(" + tag + ");", " " + writefield, "}" ); } } private void WriteFun_WriteTo_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { //所有repeated类型的tag都带长度信息 uint tag = WireFormat.MakeTag(field.Number, WireFormat.WireType.LengthDelimited); string checkfieldempty = "if (" + field.Name; string writefield = ""; checkfieldempty += ".GetCount() != 0)"; writefield = field.Name + ".WriteTo(output," + tag + ");"; fw.Write( checkfieldempty, "{", " " + writefield, "}" ); } private void WriteFun_ReadFrom(ProtoMessage message, FileWriter fw) { fw.Write( "public void ReadFrom(CodedInputStream input)", "{", " uint tag;", " while ((tag = input.ReadTag()) != 0)", " {", " switch(tag)", " {", " default:", " input.SkipLastField();", " break;" ); fw.Indent();//while fw.Indent();//switch fw.Indent(); foreach (var field in message.Fields) { if (field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_ReadFrom_RepeatedFiled(message, field, fw); } else { WriteFun_ReadFrom_Filed(message, field, fw); } } fw.Outdent(); fw.Write("}");//switch fw.Outdent(); fw.Write("}");//while fw.Outdent(); fw.Write("}");//fun fw.Write(""); } private void WriteFun_ReadFrom_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { string readfield = ""; if (field.FieldType == FieldType.Int32) { readfield = field.Name + " = input.ReadInt32();"; } if (field.FieldType == FieldType.UInt32) { readfield = field.Name + " = input.ReadUInt32();"; } if (field.FieldType == FieldType.Int64) { readfield = field.Name + " = input.ReadInt64();"; } if (field.FieldType == FieldType.UInt64) { readfield = field.Name + " = input.ReadUInt64();"; } if (field.FieldType == FieldType.Bool) { readfield = field.Name + " = input.ReadBool();"; } if (field.FieldType == FieldType.Enum) { readfield = field.Name + " = (" + field.TypeName + ")input.ReadEnum();"; } if (field.FieldType == FieldType.String || field.FieldType == FieldType.Bytes) { readfield = field.Name + ".ReadFrom(input);"; } uint tag = WireFormat.MakeTag(field.Number, field.FieldType); if (field.FieldType == FieldType.Message) { fw.Write( "case " + tag + ":", "{", " int length = input.ReadLength();", " int oldLimit = input.PushLimit(length);", " " + field.Name + ".ReadFrom(input); ", " input.CheckReadEndOfStreamTag();", " if (!input.ReachedLimit)", " {", " throw InvalidProtocolBufferException.TruncatedMessage();", " }", " input.PopLimit(oldLimit);", " break;", "}" ); } else if(field.FieldType == FieldType.String)//字符串容易溢出,特殊处理下 { fw.Write( "case " + tag + ":", "{", " try", " {", " " + readfield, " }", " catch(Exception e)", " {", " string message = \"proto message string field [" + message.Name + "." + field.Name + "] parse error, \" + e.Message;", " throw new InvalidProtocolBufferException(message);", " }", " break;", "}" ); } else { fw.Write( "case " + tag + ":", "{", " " + readfield, " break;", "}" ); } } private void WriteFun_ReadFrom_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { uint tag = WireFormat.MakeTag(field.Number, WireFormat.WireType.LengthDelimited); string readfield = field.Name + ".ReadFrom(input, " + tag + ");"; if (field.FieldType == FieldType.String)//字符串容易溢出,特殊处理下 { fw.Write( "case " + tag + ":", "{", " try", " {", " " + readfield, " }", " catch(Exception e)", " {", " string message = \"proto message string field [" + message.Name + "." + field.Name + "] parse error, \" + e.Message;", " throw new InvalidProtocolBufferException(message);", " }", " break;", "}" ); } else { fw.Write( "case " + tag + ":", "{", " " + readfield, " break;", "}" ); } } private void WriteFun_ToString(ProtoMessage message, FileWriter fw) { //空结构体,特殊处理 if (message.Fields.Count == 0) { fw.Write( "public override string ToString()", "{", " MessageFormat format = new MessageFormat();", " format.WriteLine(\"{ }\");", " return format.ToString();", "}" ); fw.Write(""); } else { fw.Write( "public override string ToString()", "{", " MessageFormat format = new MessageFormat();", " format.WriteLine(\"{\");", " format.Indent();" ); fw.Indent(); for (int i = 0; i < message.Fields.Count; i++) { var field = message.Fields[i]; bool islastField = (i == (message.Fields.Count - 1)); if (field.IsRepeated || field.FieldType == FieldType.Bytes) { WriteFun_ToString_RepeatedFiled(message, field, fw, islastField); } else { WriteFun_ToString_Filed(message, field, fw, islastField); } } fw.Write("format.Outdent();"); fw.Write("return format.ToString();"); fw.Outdent(); fw.Write("}"); fw.Write(""); } } private void WriteFun_ToString_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw, bool islastField) { if (field.FieldType == FieldType.Int32 || field.FieldType == FieldType.UInt32 || field.FieldType == FieldType.Int64 || field.FieldType == FieldType.UInt64 || field.FieldType == FieldType.Bool) { string laststr = ","; if (islastField) { laststr = " }"; } //format.WriteLine("Id : " + Id.ToString() + ","); fw.Write( "format.WriteLine(\"" + field.Name + " : \" + " + field.Name + ".ToString() + \"" + laststr + "\");" ); } else if(field.FieldType == FieldType.Enum) { string laststr = ","; if (islastField) { laststr = " }"; } //format.WriteLine("MsgId : " + MsgId.ToString() + "(" + ((int)MsgId).ToString() + "),"); fw.Write( "format.WriteLine(\"" + field.Name + " : \" + " + field.Name + ".ToString() + \"(\"" + "+((int)" + field.Name + ").ToString() + \")" + laststr + "\");" ); } else if (field.FieldType == FieldType.String)// { string laststr = ","; if (islastField) { laststr = " }"; } //format.WriteLine("Message : \"" + Message.ToString() + "\","); fw.Write( "format.WriteLine(\"" + field.Name + " : \\\"\" + " + field.Name + ".ToString() + \"\\\"" + laststr + "\");" ); } else if (field.FieldType == FieldType.Message)// { string laststr = ""; fw.Write( "format.WriteLine(\"" + field.Name + " : \" + " + field.Name + ".ToString() + \"" + laststr + "\");" ); } } private void WriteFun_ToString_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw, bool islastField) { string laststr = ","; if (islastField) { laststr = " }"; } fw.Write( "format.WriteLine(\"" + field.Name + " : \" + " + field.Name + ".ToString() + \"" + laststr + "\");" ); } #endregion public void WriteClassDesc(ProtoMessage message, FileWriter fw) { fw.Write("public class " + message.Name + " : " + "ClassMessage"); fw.Write("{"); fw.Indent(); foreach (var field in message.Fields) { string cstypename = field.FieldType == FieldType.String ? "string" : field.GetFiledTypeNameInCSharp(); if (InputParams.ClassNeedAttribute) { fw.Write("[BinarySerialize.BinSerializeMember(" + field.Number + ")]"); } if(field.IsRepeated) { string str = string.Format("public List<{0}> {1} = new List<{0}>();", cstypename, field.Name); fw.Write(str); } else if (field.FieldType == FieldType.Message) { string str = string.Format("public {0} {1} = new {0}();", cstypename, field.Name); fw.Write(str); } else { string str = string.Format("public {0} {1};", cstypename, field.Name); fw.Write(str); } } //序列化 WriteClassFun_WriteTo(message, fw); //反序列化 WriteClassFun_ReadFrom(message, fw); fw.Outdent(); fw.Write("}"); //class fw.Write(""); } private void WriteClassFun_WriteTo(ProtoMessage message, FileWriter fw) { fw.Write( "public override void WriteTo(MemoryStream output)", "{" ); fw.Indent(); foreach (var field in message.Fields) { if(field.IsRepeated || field.FieldType == FieldType.Bytes) { // WriteFun_WriteTo_RepeatedFiled(message, field, fw); WriteClassFun_WriteTo_RepeatedFiled(message, field, fw); } else { // WriteFun_WriteTo_Filed(message, field, fw); WriteClassFun_WriteTo_Filed(message, field, fw); } } fw.Outdent(); fw.Write("}"); fw.Write(""); } private void WriteClassFun_WriteTo_RepeatedFiled(ProtoMessage message, ProtoMessageField field, FileWriter fw) { // //所有repeated类型的tag都带长度信息 uint tag = WireFormat.MakeTag(field.Number, WireFormat.WireType.LengthDelimited); string writeTag = $"WriteTo_UInt32(output, {tag});"; fw.Write("\n"); if (field.FieldType == FieldType.Message) { fw.Write($"//List<{field.Message.Name}> Begin"); fw.Write($"foreach (var item in {field.Name})"); fw.Write("{"); fw.Indent(); fw.Write( writeTag, "WriteTo_Message(output, item);" ); fw.Outdent(); fw.Write("}"); fw.Write($"//List<{field.Message.Name}> End"); } else { fw.Write($"//List<{field.GetFiledTypeNameInCSharp()}> Begin"); fw.Write(writeTag); string msField = "msField" + field.Number; fw.Write($"var {msField} = Helper.GetMemoryStream();"); fw.Write($"{msField}.SetLength(0);"); fw.Write($"foreach (var item in {field.Name})"); fw.Write("{"); fw.Indent(); string line = WriteTo_Filed(field, msField); line = line.Replace(field.Name, "item"); fw.Write(line); fw.Outdent(); fw.Write("}"); string length = "length" + field.Number; fw.Write($"uint {length} = (uint){msField}.Length;"); fw.Write($"ProtocolParser.WriteUInt32(output, {length});"); fw.Write($"{msField}.WriteTo(output);"); fw.Write($"Helper.RecycleMemoryStream(ref {msField});"); fw.Write($"//List<{field.GetFiledTypeNameInCSharp()}> End"); } } private void WriteClassFun_WriteTo_Filed(ProtoMessage message, ProtoMessageField field, FileWriter fw) { uint tag = WireFormat.MakeTag(field.Number, field.FieldType); string writeTag = $"WriteTo_UInt32(output, {tag});"; if (field.FieldType == FieldType.String) // 为了和原来的协议长度对上,要不没法做检测工具 { fw.Write($"if({field.Name} != null)"); fw.Write("{"); fw.Indent(); fw.Write( writeTag, WriteTo_Filed(field) ); fw.Outdent(); fw.Write("}"); } else { fw.Write("\n"); // uint tag = WireFormat.MakeTag(field.Number, field.FieldType); fw.Write( writeTag, WriteTo_Filed(field) ); } } private string WriteTo_Filed(ProtoMessageField field, string streamOutput = "output") { string writefield = ""; if (field.FieldType == FieldType.Int32) { // writefield = "output.WriteInt32(" + field.Name + ");"; writefield = $"WriteTo_Int32({streamOutput}, {field.Name});"; } if (field.FieldType == FieldType.UInt32) { // writefield = "output.WriteUInt32(" + field.Name + ");"; writefield = $"WriteTo_UInt32({streamOutput}, {field.Name});"; } if (field.FieldType == FieldType.Int64) { // writefield = "output.WriteInt64(" + field.Name + ");"; writefield = $"WriteTo_Int64({streamOutput}, {field.Name});"; } if (field.FieldType == FieldType.UInt64) { // writefield = "output.WriteUInt64(" + field.Name + ");"; writefield = $"WriteTo_UInt64({streamOutput}, {field.Name});"; } if (field.FieldType == FieldType.Bool) { // writefield = "output.WriteBool(" + field.Name + ");"; writefield = $"WriteTo_Bool({streamOutput}, {field.Name});"; } if (field.FieldType == FieldType.Enum) { // writefield = "output.WriteEnum((int)" + field.Name + ");"; writefield = $"WriteTo_Enum({streamOutput}, (int){field.Name});"; } if (field.FieldType == FieldType.String) { // writefield = field.Name + ".WriteTo(output);"; writefield = $"WriteTo_String({streamOutput}, {field.Name});"; } if(field.FieldType == FieldType.Message) { writefield = $"WriteTo_Message({streamOutput}, {field.Name});"; } return writefield; } private void WriteClassFun_ReadFrom(ProtoMessage message, FileWriter fw) { fw.Write( "public override void ReadFrom(MemoryStream stream)", "{", " //TODO", "}" ); } } }