static void ParseFieldFlags(Field field, string flag) { switch (flag) { case "external": field.OptionExternal = true; break; case "readonly": field.OptionReadOnly = true; break; default: throw new NotImplementedException("Unknown field option: " + flag); } }
static void ParseFieldOption(Field field, string key, string value) { switch (key) { case "access": field.OptionAccess = value; break; case "codetype": field.OptionCodeType = value; break; default: throw new NotImplementedException("Unknown field option: " + key); } }
/// <summary> /// Detect field which have the same name as a submessage in the same message. /// </summary> /// <param name="m">Parent message</param> /// <param name="f">Field to check</param> void DetectNameClash(ProtoMessage m, Field f) { bool nameclash = false; if (m.CsType == f.CsName) nameclash = true; foreach (var tm in m.Messages.Values) if (tm.CsType == f.CsName) nameclash = true; foreach (var te in m.Enums.Values) if (te.CsType == f.CsName) nameclash = true; foreach (var tf in m.Fields.Values) { if (tf == f) continue; if (tf.CsName == f.CsName) nameclash = true; } if (nameclash == false) return; //Name clash if (options.FixNameclash) { if (ConvertToCamelCase) f.CsName += "Field"; else f.CsName += "_field"; Console.Error.WriteLine("Warning: renamed field: " + m.FullCsType + "." + f.CsName); //Make sure our change did not result in another name collission DetectNameClash(m, f); } else throw new ProtoFormatException("The field: " + m.FullCsType + "." + f.CsName + " has the same name as a sibling class/enum type which is not allowed in C#. " + "Use --fix-nameclash to automatically rename the field.", f.Source); }
static string FieldWriterType(Field f, string stream, string binaryWriter, string instance) { if (f.OptionCodeType != null) { switch (f.OptionCodeType) { case "DateTime": case "TimeSpan": return FieldWriterPrimitive(f, stream, binaryWriter, instance + ".Ticks"); default: //enum break; } } return FieldWriterPrimitive(f, stream, binaryWriter, instance); }
static string FieldWriterPrimitive(Field f, string stream, string binaryWriter, string instance) { if (f.ProtoType is ProtoEnum) return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt64(" + stream + ",(ulong)" + instance + ");"; if (f.ProtoType is ProtoMessage) { ProtoMessage pm = f.ProtoType as ProtoMessage; CodeWriter cw = new CodeWriter(); cw.Using("var ms" + f.ID + " = new MemoryStream()"); cw.WriteLine(pm.FullSerializerType + ".Serialize(ms" + f.ID + ", " + instance + ");"); BytesWriter(stream, "ms" + f.ID, cw); cw.EndBracket(); return cw.Code; } switch (f.ProtoType.ProtoName) { case ProtoBuiltin.Double: case ProtoBuiltin.Float: case ProtoBuiltin.Fixed32: case ProtoBuiltin.Fixed64: case ProtoBuiltin.SFixed32: case ProtoBuiltin.SFixed64: return binaryWriter + ".Write(" + instance + ");"; case ProtoBuiltin.Int32: //Serialized as 64 bit varint return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt64(" + stream + ",(ulong)" + instance + ");"; case ProtoBuiltin.Int64: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt64(" + stream + ",(ulong)" + instance + ");"; case ProtoBuiltin.UInt32: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt32(" + stream + ", " + instance + ");"; case ProtoBuiltin.UInt64: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteUInt64(" + stream + ", " + instance + ");"; case ProtoBuiltin.SInt32: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteZInt32(" + stream + ", " + instance + ");"; case ProtoBuiltin.SInt64: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteZInt64(" + stream + ", " + instance + ");"; case ProtoBuiltin.Bool: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteBool(" + stream + ", " + instance + ");"; case ProtoBuiltin.String: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteBytes(" + stream + ", Encoding.UTF8.GetBytes(" + instance + "));"; case ProtoBuiltin.Bytes: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.WriteBytes(" + stream + ", " + instance + ");"; } throw new NotImplementedException(); }
static string FieldReaderType(Field f, string stream, string binaryReader, string instance) { if (f.OptionCodeType != null) { switch (f.OptionCodeType) { case "DateTime": switch (f.ProtoType.ProtoName) { case ProtoBuiltin.UInt64: case ProtoBuiltin.Int64: case ProtoBuiltin.Fixed64: case ProtoBuiltin.SFixed64: return "new DateTime((long)" + FieldReaderPrimitive(f, stream, binaryReader, instance) + ")"; } throw new FormatException("Local feature, DateTime, must be stored in a 64 bit field"); case "TimeSpan": switch (f.ProtoType.ProtoName) { case ProtoBuiltin.UInt64: case ProtoBuiltin.Int64: case ProtoBuiltin.Fixed64: case ProtoBuiltin.SFixed64: return "new TimeSpan((long)" + FieldReaderPrimitive(f, stream, binaryReader, instance) + ")"; } throw new FormatException("Local feature, TimeSpan, must be stored in a 64 bit field"); default: //Assume enum return "(" + f.OptionCodeType + ")" + FieldReaderPrimitive(f, stream, binaryReader, instance); } } return FieldReaderPrimitive(f, stream, binaryReader, instance); }
string GenerateProperty(Field f) { string type = f.ProtoType.FullCsType; if (f.OptionCodeType != null) type = f.OptionCodeType; if (f.Rule == FieldRule.Repeated) type = "List<" + type + ">"; if (f.Rule == FieldRule.Optional && !f.ProtoType.Nullable && options.Nullable) type = type + "?"; if (f.OptionReadOnly) return f.OptionAccess + " readonly " + type + " " + f.CsName + " = new " + type + "();"; else if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType == "struct") return f.OptionAccess + " " + type + " " + f.CsName + ";"; else { string line = "private " + type + " _" + f.CsName; switch (f.ProtoType.ProtoName) { case ProtoBuiltin.Double: line += " = 0.0D"; break; case ProtoBuiltin.Float: line += " = 0.0F"; break; case ProtoBuiltin.Int32: //Wire format is 64 bit varint line += " = 0"; break; case ProtoBuiltin.Int64: line += " = 0l"; break; case ProtoBuiltin.UInt32: line += " = 0u"; break; case ProtoBuiltin.UInt64: line += " = 0ul"; break; case ProtoBuiltin.SInt32: line += " = 0"; break; case ProtoBuiltin.SInt64: line += " = 0"; break; case ProtoBuiltin.Fixed32: line += " = 0"; break; case ProtoBuiltin.Fixed64: line += " = 0"; break; case ProtoBuiltin.SFixed32: line += " = 0"; break; case ProtoBuiltin.SFixed64: line += " = 0"; break; case ProtoBuiltin.Bool: line += " = false"; break; case ProtoBuiltin.String: line += " = null"; break; } line += ";\r\n"; line += f.OptionAccess + " " + type + " " + f.CsName; line += " \r\n{"; line += "\r\n\tget\r\n\t{\r\n\t\treturn _" + f.CsName + ";\r\n\t}"; line += "\r\n\tset\r\n\t{\r\n\t\tChangedProp += \"" + f.CsName + ",\";\r\n\t\t_" + f.CsName + " = value;\r\n\t}\r\n}"; return line; } }
static void ParseFieldOption(string key, string val, Field f) { switch (key) { case "default": f.OptionDefault = val; break; case "packed": f.OptionPacked = Boolean.Parse(val); break; case "deprecated": f.OptionDeprecated = Boolean.Parse(val); break; default: Console.WriteLine("Warning: Unknown field option: " + key); break; } }
void FieldWriterType(Field f, string stream, string binaryWriter, string instance, CodeWriter cw) { if (f.OptionCodeType != null) { switch (f.OptionCodeType) { case "DateTime": if (options.Utc) { cw.WriteLine(FieldWriterPrimitive(f, stream, binaryWriter, "(" + instance + ".Kind == DateTimeKind.Utc ? " + instance + " : " + instance + ".ToUniversalTime()).Ticks")); } else cw.WriteLine(FieldWriterPrimitive(f, stream, binaryWriter, instance + ".Ticks")); return; case "TimeSpan": cw.WriteLine(FieldWriterPrimitive(f, stream, binaryWriter, instance + ".Ticks")); return; default: //enum break; } } cw.WriteLine(FieldWriterPrimitive(f, stream, binaryWriter, instance)); return; }
/// <summary> /// Generates code for writing one field /// </summary> public 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); FieldWriterType(f, "msField", "bw" + f.ID, "i" + f.ID, cw); 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(ProtocolParser.Base + ".WriteUInt32(stream, " + f.ProtoType.WireSize + "u * (uint)instance." + f.CsName + ".Count);"); cw.ForeachBracket("var i" + f.ID + " in instance." + f.CsName); FieldWriterType(f, "stream", "bw", "i" + f.ID, cw); 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); FieldWriterType(f, "stream", "bw", "i" + f.ID, cw); cw.EndBracket(); cw.EndBracket(); } return; } else if (f.Rule == FieldRule.Optional) { bool skip = options.SkipSerializeDefault && f.OptionDefault != null; 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) { if (skip) cw.IfBracket("instance." + f.CsName + " != " + f.FormatDefaultForTypeAssignment()); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "instance." + f.CsName, cw); if (skip) cw.EndBracket(); return; } if (skip) //Skip writing value if default cw.IfBracket("instance." + f.CsName + " != " + f.FormatDefaultForTypeAssignment()); KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldWriterType(f, "stream", "bw", "instance." + f.CsName, cw); if (skip) cw.EndBracket(); 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 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> /// 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 + " = " + ProtocolParser.Base + ".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; }
/// <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(ProtocolParser.Base + ".WriteUInt32(" + stream + ", length" + f.ID + ");"); cw.WriteLine("msField.WriteTo(" + stream + ");"); }
void FieldSizeWriterType(Field f, string instance, CodeWriter cw) { if (f.OptionCodeType != null) Console.WriteLine("not support"); cw.Bracket(); cw.WriteLine(FieldSizeWriterPrimitive(f, instance)); cw.EndBracket(); }
static string FieldSizeWriterPrimitive(Field f, string instance) { string numPlusLine = "num += 1u;\r\n"; if (f.ProtoType is ProtoEnum) { return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; } if (f.ProtoType is ProtoMessage) { return numPlusLine + "uint serializedSize = " + instance + ".GetSerializedSize();\r\n" + "num += serializedSize + " + ProtocolParser.Base + ".SizeOfUInt32(serializedSize);"; } switch(f.ProtoType.ProtoName) { case ProtoBuiltin.Double: case ProtoBuiltin.Float: case ProtoBuiltin.Fixed32: case ProtoBuiltin.Fixed64: case ProtoBuiltin.SFixed32: case ProtoBuiltin.SFixed64: return "unit serializedSize = 1;\r\n"; case ProtoBuiltin.Int32: //Serialized as 64 bit varint return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.Int64: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.UInt32: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.UInt64: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.SInt32: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.SInt64: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.Bool: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; case ProtoBuiltin.String: return numPlusLine + "uint byteCount = (uint)Encoding.UTF8.GetByteCount(" + instance + ");\r\n" + "num += " + ProtocolParser.Base + ".SizeOfUInt32(byteCount) + byteCount;"; case ProtoBuiltin.Bytes: return numPlusLine + "num += " + ProtocolParser.Base + ".SizeOfUInt64((ulong)" + instance + ");"; } throw new NotImplementedException(); }
public void FieldSizeWriter(ProtoMessage m, Field f, CodeWriter cw, Options options) { if (f.Rule == FieldRule.Repeated) { if (f.OptionPacked == true) { Console.WriteLine("not support"); } else { //Repeated not packet cw.IfBracket("this." + f.CsName + " != null"); cw.ForeachBracket("var i" + f.ID + " in this." + f.CsName); //KeyWriter("stream", f.ID, f.ProtoType.WireType, cw); FieldSizeWriterType(f, "i" + f.ID, cw); 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("this." + f.CsName + " != null"); FieldSizeWriterType(f, "this." + f.CsName, cw); 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("this." + f.CsName + " != " + f.ProtoType.CsType + "." + f.OptionDefault); FieldSizeWriterType(f, "this." + f.CsName, cw); if (f.OptionDefault != null) cw.EndBracket(); 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.OptionType != "struct" || f.ProtoType.ProtoName == ProtoBuiltin.String || f.ProtoType.ProtoName == ProtoBuiltin.Bytes) { cw.WriteLine("if (this." + f.CsName + " == null)"); cw.WriteIndent("throw new global::SilentOrbit.ProtocolBuffers.ProtocolBufferException(\"" + f.CsName + " is required by the proto specification.\");"); } cw.WriteLine(); FieldSizeWriterType(f, "this." + f.CsName, cw); 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); }
/// <summary> /// Prepare: ProtoType, WireType and CSType /// </summary> void PrepareProtoType(ProtoMessage m, Field f) { //Change property name to C# style, CamelCase. f.CsName = GetCSPropertyName(m, f.ProtoName); f.ProtoType = GetBuiltinProtoType(f.ProtoTypeName); if (f.ProtoType == null) f.ProtoType = Search.GetProtoType(m, f.ProtoTypeName); if (f.ProtoType == null) { #if DEBUG //this will still return null but we keep it here for debugging purposes f.ProtoType = Search.GetProtoType(m, f.ProtoTypeName); #endif throw new ProtoFormatException("Field type \"" + f.ProtoTypeName + "\" not found for field " + f.ProtoName + " in message " + m.FullProtoName, f.Source); } if (f.OptionPacked) { if (f.ProtoType.WireType == Wire.LengthDelimited) throw new ProtoFormatException("Length delimited types cannot be packed", f.Source); } }
static string FieldWriterPrimitive(Field f, string stream, string binaryWriter, string instance) { if (f.ProtoType is ProtoEnum) return ProtocolParser.Base + ".WriteUInt64(" + stream + ",(ulong)" + instance + ");"; if (f.ProtoType is ProtoMessage) { ProtoMessage pm = f.ProtoType as ProtoMessage; CodeWriter cw = new CodeWriter(); cw.WriteLine("msField.SetLength(0);"); cw.WriteLine(pm.FullSerializerType + ".Serialize(msField, " + instance + ");"); BytesWriter(f, stream, cw); return cw.Code; } switch (f.ProtoType.ProtoName) { case ProtoBuiltin.Double: case ProtoBuiltin.Float: case ProtoBuiltin.Fixed32: case ProtoBuiltin.Fixed64: case ProtoBuiltin.SFixed32: case ProtoBuiltin.SFixed64: return binaryWriter + ".Write(" + instance + ");"; case ProtoBuiltin.Int32: //Serialized as 64 bit varint return ProtocolParser.Base + ".WriteUInt64(" + stream + ",(ulong)" + instance + ");"; case ProtoBuiltin.Int64: return ProtocolParser.Base + ".WriteUInt64(" + stream + ",(ulong)" + instance + ");"; case ProtoBuiltin.UInt32: return ProtocolParser.Base + ".WriteUInt32(" + stream + ", " + instance + ");"; case ProtoBuiltin.UInt64: return ProtocolParser.Base + ".WriteUInt64(" + stream + ", " + instance + ");"; case ProtoBuiltin.SInt32: return ProtocolParser.Base + ".WriteZInt32(" + stream + ", " + instance + ");"; case ProtoBuiltin.SInt64: return ProtocolParser.Base + ".WriteZInt64(" + stream + ", " + instance + ");"; case ProtoBuiltin.Bool: return ProtocolParser.Base + ".WriteBool(" + stream + ", " + instance + ");"; case ProtoBuiltin.String: return ProtocolParser.Base + ".WriteBytes(" + stream + ", Encoding.UTF8.GetBytes(" + instance + "));"; case ProtoBuiltin.Bytes: return ProtocolParser.Base + ".WriteBytes(" + stream + ", " + instance + ");"; } throw new NotImplementedException(); }
string GenerateProperty(Field f) { string type = f.ProtoType.FullCsType; if (f.OptionCodeType != null) type = f.OptionCodeType; if (f.Rule == FieldRule.Repeated) type = "List<" + type + ">"; if (f.Rule == FieldRule.Optional && !f.ProtoType.Nullable && options.Nullable) type = type + "?"; if (f.OptionReadOnly) return f.OptionAccess + " readonly " + type + " " + f.CsName + " = new " + type + "();"; else if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType == "struct") return f.OptionAccess + " " + type + " " + f.CsName + ";"; else return f.OptionAccess + " " + type + " " + f.CsName + " { get; set; }"; }
/// <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 bool ParseField(TokenReader tr, ProtoMessage m) { string rule = tr.ReadNext(); while (true) { if (ParseComment(rule) == false) break; rule = tr.ReadNext(); } Field f = new Field(tr); //Rule switch (rule) { case ";": lastComment.Clear(); return true; case "}": lastComment.Clear(); return false; case "required": f.Rule = FieldRule.Required; break; case "optional": f.Rule = FieldRule.Optional; break; case "repeated": f.Rule = FieldRule.Repeated; break; case "option": //Save options ParseOption(tr, m); return true; case "message": ParseMessage(tr, m, m.Package + "." + m.ProtoName); return true; case "enum": ParseEnum(tr, m, m.Package + "." + m.ProtoName); return true; case "extensions": ParseExtensions(tr, m); return true; default: throw new ProtoFormatException("unknown rule: " + rule, tr); } //Field comments LocalParser.ParseComments(f, lastComment, tr); //Type f.ProtoTypeName = tr.ReadNext(); //Name f.ProtoName = tr.ReadNext(); //ID tr.ReadNextOrThrow("="); f.ID = int.Parse(tr.ReadNext()); if (19000 <= f.ID && f.ID <= 19999) throw new ProtoFormatException("Can't use reserved field ID 19000-19999", tr); if (f.ID > (1 << 29) - 1) throw new ProtoFormatException("Maximum field id is 2^29 - 1", tr); //Add Field to message m.Fields.Add(f.ID, f); //Determine if extra options string extra = tr.ReadNext(); if (extra == ";") return true; //Field options if (extra != "[") throw new ProtoFormatException("Expected: [ got " + extra, tr); ParseFieldOptions(tr, f); return true; }
static string FieldReaderPrimitive(Field f, string stream, string binaryReader, string instance) { if (f.ProtoType is ProtoMessage) { var m = f.ProtoType as ProtoMessage; if (f.Rule == FieldRule.Repeated || instance == null) return m.FullSerializerType + ".DeserializeLengthDelimited(" + stream + ")"; else return m.FullSerializerType + ".DeserializeLengthDelimited(" + stream + ", " + instance + ")"; } if (f.ProtoType is ProtoEnum) return "(" + f.ProtoType.FullCsType + ")global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt64(" + stream + ")"; if (f.ProtoType is ProtoBuiltin) { switch (f.ProtoType.ProtoName) { case ProtoBuiltin.Double: return binaryReader + ".ReadDouble()"; case ProtoBuiltin.Float: return binaryReader + ".ReadSingle()"; case ProtoBuiltin.Int32: //Wire format is 64 bit varint return "(int)global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt64(" + stream + ")"; case ProtoBuiltin.Int64: return "(long)global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt64(" + stream + ")"; case ProtoBuiltin.UInt32: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt32(" + stream + ")"; case ProtoBuiltin.UInt64: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadUInt64(" + stream + ")"; case ProtoBuiltin.SInt32: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadZInt32(" + stream + ")"; case ProtoBuiltin.SInt64: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadZInt64(" + stream + ")"; case ProtoBuiltin.Fixed32: return binaryReader + ".ReadUInt32()"; case ProtoBuiltin.Fixed64: return binaryReader + ".ReadUInt64()"; case ProtoBuiltin.SFixed32: return binaryReader + ".ReadInt32()"; case ProtoBuiltin.SFixed64: return binaryReader + ".ReadInt64()"; case ProtoBuiltin.Bool: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadBool(" + stream + ")"; case ProtoBuiltin.String: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadString(" + stream + ")"; case ProtoBuiltin.Bytes: return "global::SilentOrbit.ProtocolBuffers.ProtocolParser.ReadBytes(" + stream + ")"; default: throw new ProtoFormatException("unknown build in: " + f.ProtoType.ProtoName, f.Source); } } throw new NotImplementedException(); }
static void ParseFieldOptions(TokenReader tr, Field f) { while (true) { string key = tr.ReadNext(); tr.ReadNextOrThrow("="); string val = tr.ReadNext(); ParseFieldOption(key, val, f); string optionSep = tr.ReadNext(); if (optionSep == "]") break; if (optionSep == ",") continue; throw new ProtoFormatException(@"Expected "","" or ""]"" got " + tr.NextCharacter, tr); } tr.ReadNextOrThrow(";"); }
static string GenerateProperty(Field f) { string type = f.ProtoType.FullCsType; if (f.ProtoType is ProtoEnum) { type = "int"; } if (f.OptionCodeType != null) type = f.OptionCodeType; if (f.Rule == FieldRule.Repeated) type = type + "[]"; if (f.OptionReadOnly) return f.OptionAccess + " readonly " + type + " " + f.CsName + " = new " + type + "();"; else if (f.ProtoType is ProtoMessage && f.ProtoType.OptionType == "struct") return f.OptionAccess + " " + type + " " + f.CsName + ";"; else { string s = "\n#if !CITO\n internal\n#endif\n " + type + " " + f.CsName + ";" + Environment.NewLine; s += f.OptionAccess + " " + type + " " + "Get" + f.CsName + "() { return " + f.CsName + "; } " + Environment.NewLine; if (f.Rule != FieldRule.Repeated) { s += f.OptionAccess + " void Set" + f.CsName + "(" + type + " value) { " + f.CsName + " = " + "value; } " + Environment.NewLine; } else { s += f.OptionAccess + " void Set" + f.CsName + "(" + type + " value, int count, int length) { " + f.CsName + " = " + "value; " + f.CsName + "Count = count; " + f.CsName + "Length = length; } " + Environment.NewLine; s += "\n#if !CITO\n internal\n#endif\n int" + " " + f.CsName + "Count;" + Environment.NewLine; s += f.OptionAccess + " int Get" + f.CsName + "Count() { return " + f.CsName + "Count; } " + Environment.NewLine; s += "\n#if !CITO\n internal\n#endif\n int" + " " + f.CsName + "Length;" + Environment.NewLine; s += f.OptionAccess + " int Get" + f.CsName + "Length() { return " + f.CsName + "Length; } " + Environment.NewLine; s += f.OptionAccess + " void " + f.CsName + "Add(" + type.Replace("[]", "") + " value)"; s += string.Format("{{if({0}Count >= {0}Length)\n", f.CsName); s += "{\n"; s += string.Format("{0}[] {1}2 = new {0}[{1}Length*2];\n", type.Replace("[]", ""), f.CsName); s += string.Format("{0}Length = {0}Length*2;\n", f.CsName); s += string.Format("for(int i=0;i<{0}Count;i++)\n", f.CsName); s += "{\n"; s += string.Format("{0}2[i] = {0}[i];\n", f.CsName); s += "}\n"; s += string.Format("{0}={0}2;\n", f.CsName); s += "}\n"; s += string.Format("{0}[{0}Count] = value;\n", f.CsName); s += string.Format("{0}Count++;\n", f.CsName); s += "}" + Environment.NewLine; } return s; } }