/// <summary> /// Generates code for writing one field /// </summary> public void FieldWriter(ProtoMessage m, Field f, CodeWriter cw, Options options) { if (f.Rule == FieldRule.Repeated) { //Repeated not packet cw.IfBracket("instance." + f.CsName + " != null"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "i" + f.ID, cw); cw.EndBracket(); cw.EndBracket(); } else if (f.Rule == FieldRule.Optional) { if (options.Nullable || f.ProtoType is ProtoMessage || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { if (f.ProtoType.Nullable || options.Nullable) //Struct always exist, not optional { cw.IfBracket("instance." + f.CsName + " != null"); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); var needValue = !f.ProtoType.Nullable && options.Nullable; FieldWriterType(f, "stream", "bw", "instance." + f.CsName + (needValue ? ".Value" : ""), cw); if (f.ProtoType.Nullable || options.Nullable) //Struct always exist, not optional { cw.EndBracket(); } return; } if (f.ProtoType is ProtoEnum) { KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "instance." + f.CsName, cw); return; } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "instance." + f.CsName, cw); return; } else if (f.Rule == FieldRule.Required) { if (f.ProtoType is ProtoMessage || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"" + f.CsName + " is required by the proto specification.\");"); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "instance." + f.CsName, cw); return; } throw new NotImplementedException("Unknown rule: " + f.Rule); }
/// <summary> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw, Options options) { string stack = "global::SilentOrbit.ProtocolBuffers.ProtocolParser.Stack"; if (options.ExperimentalStack != null) { cw.WriteLine("[ThreadStatic]"); cw.WriteLine("static global::SilentOrbit.ProtocolBuffers.MemoryStreamStack stack = new " + options.ExperimentalStack + "();"); stack = "stack"; } cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(Stream stream, " + m.CsType + " instance)"); if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } if (m.IsUsingBinaryWriter) { cw.WriteLine("BinaryWriter bw = new BinaryWriter(stream);"); } //Shared memorystream for all fields cw.WriteLine("var msField = " + stack + ".Pop();"); foreach (Field f in m.Fields.Values) { FieldSerializer.FieldWriter(m, f, cw); } cw.WriteLine(stack + ".Push(msField);"); if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } cw.EndBracket(); cw.WriteLine(); cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.CsType + " instance)"); cw.Using("var ms = new MemoryStream()"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("return ms.ToArray();"); cw.EndBracket(); cw.EndBracket(); cw.Summary("Helper: Serialize with a varint length prefix"); cw.Bracket(m.OptionAccess + " static void SerializeLengthDelimited(Stream stream, " + m.CsType + " instance)"); cw.WriteLine("var data = SerializeToBytes(instance);"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(stream, (uint)data.Length);"); cw.WriteLine("stream.Write(data, 0, data.Length);"); cw.EndBracket(); }
/// <summary> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw) { cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(CitoStream stream, " + m.FullCsType + " instance)"); if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } if (m.IsUsingBinaryWriter) { cw.WriteLine("BinaryWriter bw = new BinaryWriter(stream);"); } foreach (Field f in m.Fields.Values) { FieldSerializer.FieldWriter(m, f, cw); } if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } cw.EndBracket(); cw.WriteLine(); cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.FullCsType + " instance)"); cw.WriteLine("CitoMemoryStream ms = new CitoMemoryStream();"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("return ms.ToArray();"); //cw.EndBracket(); cw.EndBracket(); cw.Summary("Helper: Serialize with a varint length prefix"); cw.Bracket(m.OptionAccess + " static void SerializeLengthDelimited(CitoStream stream, " + m.FullCsType + " instance)"); cw.WriteLine("byte[] data = SerializeToBytes(instance);"); cw.WriteLine("ProtocolParser.WriteUInt32_(stream, ProtoPlatform.ArrayLength(data));"); cw.WriteLine("stream.Write(data, 0, ProtoPlatform.ArrayLength(data));"); cw.EndBracket(); }
/// <summary> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw) { cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(Stream stream, " + m.CsType + " instance)"); if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } if (m.IsUsingBinaryWriter) { cw.WriteLine("BinaryWriter bw = new BinaryWriter(stream);"); } foreach (Field f in m.Fields.Values) { FieldSerializer.FieldWriter(m, f, cw); } if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } cw.EndBracket(); cw.WriteLine(); cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.CsType + " instance)"); cw.Using("var ms = new MemoryStream()"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("return ms.ToArray();"); cw.EndBracket(); cw.EndBracket(); }
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> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw) { cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(CitoStream stream, " + m.FullCsType + " instance)"); if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } if (m.IsUsingBinaryWriter) cw.WriteLine("BinaryWriter bw = new BinaryWriter(stream);"); foreach (Field f in m.Fields.Values) FieldSerializer.FieldWriter(m, f, cw); if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } cw.EndBracket(); cw.WriteLine(); cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.FullCsType + " instance)"); cw.WriteLine("CitoMemoryStream ms = new CitoMemoryStream();"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("return ms.ToArray();"); //cw.EndBracket(); cw.EndBracket(); cw.Summary("Helper: Serialize with a varint length prefix"); cw.Bracket(m.OptionAccess + " static void SerializeLengthDelimited(CitoStream stream, " + m.FullCsType + " instance)"); cw.WriteLine("byte[] data = SerializeToBytes(instance);"); cw.WriteLine("ProtocolParser.WriteUInt32_(stream, ProtoPlatform.ArrayLength(data));"); cw.WriteLine("stream.Write(data, 0, ProtoPlatform.ArrayLength(data));"); cw.EndBracket(); }
/// <summary> /// Generates code for writing one field /// </summary> public static void FieldWriter(ProtoMessage m, Field f, CodeWriter cw, Options options) { if (f.Rule == FieldRule.Repeated) { if (f.OptionPacked == true) { //Repeated packed cw.IfBracket("instance." + f.CsName + " != null"); KeyWriter("stream", f.ID, Wire.LengthDelimited, cw); if (f.ProtoType.WireSize < 0) { //Un-optimized, unknown size cw.WriteLine("msField.SetLength(0);"); if (f.IsUsingBinaryWriter) { cw.WriteLine("BinaryWriter bw" + f.ID + " = new BinaryWriter(ms" + f.ID + ");"); } cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); cw.WriteLine(FieldWriterType(f, "msField", "bw" + f.ID, "i" + f.ID)); cw.EndBracket(); BytesWriter(f, "stream", cw); } else { //Optimized with known size //No memorystream buffering, write size first at once //For constant size messages we can skip serializing to the MemoryStream cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(stream, " + f.ProtoType.WireSize + "u * (uint)instance." + f.CsName + ".Count);"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); cw.WriteLine(FieldWriterType(f, "stream", "bw", "i" + f.ID)); cw.EndBracket(); } cw.EndBracket(); } else { //Repeated not packet cw.IfBracket("instance." + f.CsName + " != null"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "i" + f.ID)); cw.EndBracket(); cw.EndBracket(); } return; } else if (f.Rule == FieldRule.Optional) { if (options.Nullable || f.ProtoType is ProtoMessage || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { if (f.ProtoType.Nullable || options.Nullable) //Struct always exist, not optional { cw.IfBracket("instance." + f.CsName + " != null"); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); var needValue = !f.ProtoType.Nullable && options.Nullable; cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName + (needValue ? ".Value" : ""))); if (f.ProtoType.Nullable || options.Nullable) //Struct always exist, not optional { cw.EndBracket(); } return; } if (f.ProtoType is ProtoEnum) { if (f.OptionDefault != null) { cw.IfBracket("instance." + f.CsName + " != " + f.ProtoType.CsType + "." + f.OptionDefault); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); if (f.OptionDefault != null) { cw.EndBracket(); } return; } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); return; } else if (f.Rule == FieldRule.Required) { if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType != "struct" || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("throw new ArgumentNullException(\"" + f.CsName + "\", \"Required by proto specification.\");"); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); return; } throw new NotImplementedException("Unknown rule: " + f.Rule); }
/// <summary> /// Generates code for writing one field /// </summary> public static void FieldWriter(ProtoMessage m, Field f, CodeWriter cw) { if (f.Rule == FieldRule.Repeated) { if (f.OptionPacked == true) { //Repeated packed cw.IfBracket("instance." + f.CsName + " != null"); KeyWriter("stream", f.ID, Wire.LengthDelimited, cw); if (f.ProtoType.WireSize < 0) { //Un-optimized, unknown size cw.Using("var ms" + f.ID + " = new MemoryStream()"); if (f.IsUsingBinaryWriter) cw.WriteLine("BinaryWriter bw" + f.ID + " = new BinaryWriter(ms" + f.ID + ");"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); cw.WriteLine(FieldWriterType(f, "ms" + f.ID, "bw" + f.ID, "i" + f.ID)); cw.EndBracket(); BytesWriter("stream", "ms" + f.ID, cw); cw.EndBracket(); } else { //Optimized with known size //No memorystream buffering, write size first at once //For constant size messages we can skip serializing to the MemoryStream cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(stream, " + f.ProtoType.WireSize + "u * (uint)instance." + f.CsName + ".Count);"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); cw.WriteLine(FieldWriterType(f, "stream", "bw", "i" + f.ID)); cw.EndBracket(); } cw.EndBracket(); } else { //Repeated not packet cw.IfBracket("instance." + f.CsName + " != null"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "i" + f.ID)); cw.EndBracket(); cw.EndBracket(); } return; } else if (f.Rule == FieldRule.Optional) { if (f.ProtoType is ProtoMessage || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { if (f.ProtoType.Nullable) //Struct always exist, not optional cw.IfBracket("instance." + f.CsName + " != null"); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); if (f.ProtoType.Nullable) //Struct always exist, not optional cw.EndBracket(); return; } if (f.ProtoType is ProtoEnum) { if (f.OptionDefault != null) cw.IfBracket("instance." + f.CsName + " != " + f.ProtoType.CsType + "." + f.OptionDefault); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); if (f.OptionDefault != null) cw.EndBracket(); return; } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); return; } else if (f.Rule == FieldRule.Required) { if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType != "struct" || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("throw new ArgumentNullException(\"" + f.CsName + "\", \"Required by proto specification.\");"); } KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); cw.WriteLine(FieldWriterType(f, "stream", "bw", "instance." + f.CsName)); return; } throw new NotImplementedException("Unknown rule: " + f.Rule); }
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> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw, Options options) { cw.WriteLine("#region [Methods] Writer"); #region [Method] Serialize cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(Stream stream, " + m.CsType + " instance)"); { if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } //Shared memorystream for all fields cw.WriteLine("var msField = Pool.Get<MemoryStream>();"); foreach (Field f in m.Fields.Values) { FieldSerializer.FieldWriter(m, f, cw, false); } cw.WriteLine("Pool.FreeMemoryStream(ref msField);"); if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } } cw.EndBracketSpace(); #endregion #region [Method] SerializeToBytes cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.CsType + " instance)"); { cw.WriteLine("var ms = Pool.Get<MemoryStream>();"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("var arr = ms.ToArray();"); cw.WriteLine("Pool.FreeMemoryStream(ref ms);"); cw.WriteLine("return arr;"); } cw.EndBracket(); #endregion #region [Method] SerializeLengthDelimited cw.Summary("Helper: Serialize with a varint length prefix"); cw.Bracket(m.OptionAccess + " static void SerializeLengthDelimited(Stream stream, " + m.CsType + " instance)"); { cw.WriteLine("var data = SerializeToBytes(instance);"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(stream, (uint)data.Length);"); cw.WriteLine("stream.Write(data, 0, data.Length);"); } cw.EndBracketSpace(); #endregion #region [Method] SerializeDelta cw.Bracket($"public static void SerializeDelta(Stream stream, {m.CsType} instance, {m.CsType} previous)"); { cw.WriteLine("var msField = Pool.Get<MemoryStream>();"); foreach (Field f in m.Fields.Values) { cw.IfBracket($"instance.{f.CsName} != previous.{f.CsName}"); FieldSerializer.FieldWriter(m, f, cw, true); cw.EndBracket(); } cw.WriteLine("Pool.FreeMemoryStream(ref msField);"); } cw.EndBracket(); #endregion #region [Method] WriteToStream if (m.OptionExternal == false) { cw.Bracket("public void WriteToStream(Stream stream)"); { cw.WriteLine("Serialize(stream, this);"); } cw.EndBracket(); } #endregion #region [Method] WriteToStreamDelta if (m.OptionExternal == false) { cw.Bracket($"public void WriteToStreamDelta(Stream stream, {m.CsType} previous)"); { cw.IfBracket("previous != null"); { cw.WriteLine("SerializeDelta(stream, this, previous);"); } cw.EndBracket(); cw.Bracket("else"); { cw.WriteLine("Serialize(stream, this);"); } cw.EndBracket(); } cw.EndBracket(); } #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> /// Generates code for writing a class/message /// </summary> static void GenerateWriter(ProtoMessage m, CodeWriter cw) { cw.Summary("Serialize the instance into the stream"); cw.Bracket(m.OptionAccess + " static void Serialize(Stream stream, " + m.CsType + " instance)"); if (m.OptionTriggers) { cw.WriteLine("instance.BeforeSerialize();"); cw.WriteLine(); } if (m.IsUsingBinaryWriter) cw.WriteLine("BinaryWriter bw = new BinaryWriter(stream);"); foreach (Field f in m.Fields.Values) FieldSerializer.FieldWriter(m, f, cw); if (m.OptionPreserveUnknown) { cw.IfBracket("instance.PreservedFields != null"); cw.ForeachBracket("var kv in instance.PreservedFields"); cw.WriteLine("global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteKey(stream, kv.Key);"); cw.WriteLine("stream.Write(kv.Value, 0, kv.Value.Length);"); cw.EndBracket(); cw.EndBracket(); } cw.EndBracket(); cw.WriteLine(); cw.Summary("Helper: Serialize into a MemoryStream and return its byte array"); cw.Bracket(m.OptionAccess + " static byte[] SerializeToBytes(" + m.CsType + " instance)"); cw.Using("var ms = new MemoryStream()"); cw.WriteLine("Serialize(ms, instance);"); cw.WriteLine("return ms.ToArray();"); cw.EndBracket(); cw.EndBracket(); }
/// <summary> /// Generates code for writing one field as a JSON string /// </summary> public static void FieldJsonWriter(ProtoMessage m, Field f, CodeWriter cw, Options options) { cw.WriteLine("writer.Write(\"\\\"" + f.ProtoName + "\\\":\");"); if (f.Rule == FieldRule.Repeated) { cw.WriteLine(); cw.IfBracket("instance." + f.CsName + " != null"); cw.WriteLine("writer.Write(\"[\");"); cw.WriteLine("var first = true;"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); cw.WriteLine("if (!first) writer.Write(\",\");"); cw.WriteLine("else first = false;"); cw.WriteLine(FieldWriterJsonType(f, m.RequiredMessageTables, "writer", "i" + f.ID)); cw.EndBracket(); cw.WriteLine("writer.Write(\"]\");"); cw.ElseBracket(); cw.WriteLine("writer.Write(\"null\");"); cw.EndBracket(); return; } else if (f.Rule == FieldRule.Optional) { if (options.Nullable || f.ProtoType is ProtoMessage || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { var nullable = (f.ProtoType.Nullable || options.Nullable) && !(f.ProtoType.OptionType == "interface" && m.RequiredMessageTables.Contains(f.ProtoType)); if (nullable) //Struct always exist, not optional { cw.IfBracket("instance." + f.CsName + " != null"); } var needValue = !f.ProtoType.Nullable && options.Nullable; cw.WriteLine(FieldWriterJsonType(f, m.RequiredMessageTables, "writer", "instance." + f.CsName + (needValue ? ".Value" : ""))); if (nullable) //Struct always exist, not optional { cw.ElseBracket(); cw.WriteLine("writer.Write(\"null\");"); cw.EndBracket(); } return; } if (f.ProtoType is ProtoEnum) { cw.WriteLine(FieldWriterJsonType(f, m.RequiredMessageTables, "writer", "instance." + f.CsName)); return; } cw.WriteLine(FieldWriterJsonType(f, m.RequiredMessageTables, "writer", "instance." + f.CsName)); return; } else if (f.Rule == FieldRule.Required) { if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType != "struct" || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { cw.WriteLine("if (instance." + f.CsName + " == null)"); cw.WriteIndent("throw new ArgumentNullException(\"" + f.CsName + "\", \"Required by proto specification.\");"); } cw.WriteLine(FieldWriterJsonType(f, m.RequiredMessageTables, "writer", "instance." + f.CsName)); return; } throw new NotImplementedException("Unknown rule: " + f.Rule); }
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; }