static void ParseEnum(TokenReader tr, ProtoMessage parent, string package) { ProtoEnum me = new ProtoEnum(parent, package); LocalParser.ParseComments(me, lastComment, tr); me.ProtoName = tr.ReadNext(); parent.Enums.Add(me.ProtoName, me); //must be after .ProtoName is read if (tr.ReadNext() != "{") throw new ProtoFormatException("Expected: {", tr); while (true) { string name = tr.ReadNext(); if (ParseComment(name)) continue; if (name == "}") return; //Ignore options if (name == "option") { ParseOption(tr, null); lastComment.Clear(); continue; } ParseEnumValue(tr, me, name); } }
public void GenerateClassSerializer(ProtoMessage m) { if (options.NoGenerateImported && m.IsImported) { Console.Error.WriteLine("Skipping imported " + m.FullProtoName); return; } if (m.OptionExternal || m.OptionType == "interface") { //Don't make partial class of external classes or interfaces //Make separate static class for them cw.Bracket(m.OptionAccess + " static class " + m.SerializerType); } else { if (options.SerializableAttributes) cw.Attribute("System.Serializable"); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.SerializerType); } GenerateReader(m); GenerateWriter(m); foreach (ProtoMessage sub in m.Messages.Values) { cw.WriteLine(); GenerateClassSerializer(sub); } cw.EndBracket(); cw.WriteLine(); return; }
/// <summary> /// Generates the properties. /// </summary> /// <param name='template'> /// if true it will generate only properties that are not included by default, because of the [generate=false] option. /// </param> static void GenerateProperties(ProtoMessage m, CodeWriter cw) { foreach (Field f in m.Fields.Values) { if (f.OptionExternal) cw.WriteLine("//" + GenerateProperty(f) + " // Implemented by user elsewhere"); else { if (f.Comments != null) cw.Summary(f.Comments); cw.WriteLine(GenerateProperty(f)); cw.WriteLine(); } } //Wire format field ID #if DEBUG cw.Comment("ProtocolBuffers wire field id"); foreach (Field f in m.Fields.Values) { cw.WriteLine("public const int " + f.CsName + "FieldID = " + f.ID + ";"); } #endif }
public static void GenerateClassSerializer(ProtoMessage m, CodeWriter cw) { if (m.OptionExternal || m.OptionType == "interface") { //Don't make partial class of external classes or interfaces //Make separate static class for them cw.Bracket(m.OptionAccess + " static class " + m.SerializerType); } else { cw.Attribute("System.Serializable()"); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.SerializerType); } GenerateReader(m, cw); GenerateWriter(m, cw); foreach (ProtoMessage sub in m.Messages.Values) { cw.WriteLine(); GenerateClassSerializer(sub, cw); } cw.EndBracket(); cw.WriteLine(); return; }
static void GenerateEnums(ProtoMessage m, CodeWriter cw) { foreach (ProtoEnum me in m.Enums.Values) { GenerateEnum(me, cw); } }
void PrepareMessage(ProtoMessage m) { //Name of message and enums m.CsType = GetCamelCase(m.ProtoName); foreach (ProtoEnum e in m.Enums.Values) { e.CsType = GetCamelCase(e.ProtoName); } foreach (ProtoMessage sub in m.Messages.Values) PrepareMessage(sub); //Prepare fields foreach (Field f in m.Fields.Values) { PrepareProtoType(m, f); DetectNameClash(m, f); if (f.OptionDefault != null) { if (f.ProtoType is ProtoBuiltin && ((ProtoBuiltin)f.ProtoType).ProtoName == "bytes") throw new NotImplementedException(); if (f.ProtoType is ProtoMessage) throw new ProtoFormatException("Message can't have a default", f.Source); } } }
public void GenerateClass(ProtoMessage m) { if (options.NoGenerateImported && m.IsImported) { Console.Error.WriteLine("Skipping imported " + m.FullProtoName); return; } //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType); if (options.GenerateDefaultConstructors) GenerateCtorForDefaults(m); GenerateEnums(m); GenerateProperties(m); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub); cw.WriteLine(); } cw.EndBracket(); return; }
/// <summary> /// Used by types within a namespace /// </summary> public ProtoType(ProtoMessage parent, string package) : this() { if (this is ProtoCollection == false) { if (parent == null) throw new ArgumentNullException("parent"); if (package == null) throw new ArgumentNullException("package"); } this.Parent = parent; this.Package = package; }
static void ParseMessageFlags(ProtoMessage message, string flag) { switch (flag) { case "triggers": message.OptionTriggers = true; break; case "preserveunknown": message.OptionPreserveUnknown = true; break; case "external": message.OptionExternal = true; break; default: throw new NotImplementedException("Unknown option: " + flag); } }
public static void GenerateClass(ProtoMessage m, CodeWriter cw, Options options) { //Do not generate class code for external classes if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsType + " {}"); return; } //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " partial " + m.OptionType + " " + m.CsType); GenerateEnums(m, cw); GenerateProperties(m, cw); //if(options.GenerateToString... // ... if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<global::SilentOrbit.ProtocolBuffers.KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw, options); cw.WriteLine(); } cw.EndBracket(); return; }
static void ParseMessageOption(ProtoMessage message, string key, string value) { //Parse value switch (key) { case "namespace": message.OptionNamespace = value; break; case "access": message.OptionAccess = value; break; case "type": message.OptionType = value; break; case "buffer": message.BufferSize = int.Parse(value); break; default: throw new NotImplementedException("Unknown 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); }
public static void GenerateClass(ProtoMessage m, CodeWriter cw) { //Do not generate class code for external classes /*if (m.OptionExternal) { cw.Comment("Written elsewhere"); cw.Comment(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType + " {}"); return; }*/ //Default class cw.Summary(m.Comments); cw.Bracket(m.OptionAccess + " " + m.OptionType + " " + m.CsNamespace + "_" + m.CsType); GenerateEnums(m, cw); GenerateProperties(m, cw); if (m.OptionPreserveUnknown) { cw.Summary("Values for unknown fields."); cw.WriteLine("public List<KeyValue> PreservedFields;"); cw.WriteLine(); } if (m.OptionTriggers) { cw.Comment("protected virtual void BeforeSerialize() {}"); cw.Comment("protected virtual void AfterDeserialize() {}"); cw.WriteLine(); } foreach (ProtoMessage sub in m.Messages.Values) { GenerateClass(sub, cw); cw.WriteLine(); } cw.EndBracket(); return; }
static ProtoType SearchSubMessages(ProtoMessage msg, string fullPath) { foreach (ProtoMessage sub in msg.Messages.Values) { if (fullPath == sub.FullProtoName) return sub; if (fullPath.StartsWith(sub.FullProtoName + ".")) { ProtoType pt = SearchSubMessages(sub, fullPath); if (pt != null) return pt; } } foreach (ProtoEnum subEnum in msg.Enums.Values) { if (fullPath == subEnum.FullProtoName) return subEnum; } return null; }
/// <summary> /// Search for message in hierarchy /// </summary> public static ProtoType GetProtoType(ProtoMessage msg, string path) { //Search for message or enum ProtoType pt; //Search from one level up until a match is found while (msg is ProtoCollection == false) { //Search sub messages pt = SearchSubMessages(msg, msg.Package + "." + msg.ProtoName + "." + path); if (pt != null) return pt; //Search siblings pt = SearchSubMessages(msg.Parent, msg.Package + "." + path); if (pt != null) return pt; msg = msg.Parent; } //Finally search for global namespace return SearchSubMessages(msg, path); }
void GenerateCtorForDefaults(ProtoMessage m) { // Collect all fields with default values. var fieldsWithDefaults = new List<Field>(); foreach (Field field in m.Fields.Values) { if (field.OptionDefault != null) { fieldsWithDefaults.Add(field); } } if (fieldsWithDefaults.Count > 0) { cw.Bracket("public " + m.CsType + "()"); foreach (var field in fieldsWithDefaults) { string formattedValue = field.FormatDefaultForTypeAssignment(); string line = string.Format("{0} = {1};", field.CsName, formattedValue); cw.WriteLine(line); } cw.EndBracket(); } }
static void ParseMessage(TokenReader tr, ProtoMessage parent, string package) { var msg = new ProtoMessage(parent, package); LocalParser.ParseComments(msg, lastComment, tr); msg.ProtoName = tr.ReadNext(); tr.ReadNextOrThrow("{"); try { while (ParseField(tr, msg)) continue; } catch (Exception e) { throw new ProtoFormatException(e.Message, e, tr); } parent.Messages.Add(msg.ProtoName, msg); }
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 void ParseExtensions(TokenReader tr, ProtoMessage m) { //extensions 100 to max; tr.ReadNext(); //100 tr.ReadNextOrThrow("to"); tr.ReadNext(); //number or max tr.ReadNextOrThrow(";"); }
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) { //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) { if (f.ProtoType is ProtoEnum) cw.WriteLine("instance." + f.CsName + " = " + f.ProtoType.FullCsType + "." + 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("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 InvalidOperationException(\"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 InvalidDataException(\"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) { 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.Using("var msField = new MemoryStream(" + m.MaxFieldBufferSize() + ")"); foreach (Field f in m.Fields.Values) FieldSerializer.FieldWriter(m, f, cw); cw.EndBracket(); 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> private void GenerateWriter(ProtoMessage m) { 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.Using("var bw = new BinaryWriter(stream, Encoding.UTF8, true)"); //Shared memorystream for all fields cw.WriteLine("var msField = " + stack + ".Pop();"); foreach (Field f in m.Fields.Values) { if (f.OptionDeprecated) cw.WritePragma("warning disable 612"); fieldSerializer.FieldWriter(m, f, cw, options); if (f.OptionDeprecated) cw.WritePragma("warning restore 612"); } 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(); } if (m.IsUsingBinaryWriter) 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(); }
public ProtoMessage(ProtoMessage parent, string package) : base(parent, package) { this.OptionType = "class"; }
void GenerateEnums(ProtoMessage m) { foreach (ProtoEnum me in m.Enums.Values) { GenerateEnum(me); } }
/// <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> /// File or Message options /// </summary> static void ParseOption(TokenReader tr, ProtoMessage m) { //Read name string key = tr.ReadNext(); if (tr.ReadNext() != "=") throw new ProtoFormatException("Expected: = got " + tr.NextCharacter, tr); //Read value string value = tr.ReadNext(); if (tr.ReadNext() != ";") throw new ProtoFormatException("Expected: ; got " + tr.NextCharacter, tr); //null = ignore option if (m == null) return; switch (key) { //None at the moment //case "namespace": // m.OptionNamespace = value; // break; default: Console.WriteLine("Warning: Unknown option: " + key + " = " + value); break; } }
public ProtoEnum(ProtoMessage parent, string package) : base(parent, package) { }
/// <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); } }
/// <summary> /// Gets the C# CamelCase version of a given name. /// Name collisions with enums are avoided. /// </summary> string GetCSPropertyName(ProtoMessage m, string name) { string csname = GetCamelCase(name); foreach (ProtoEnum me in m.Enums.Values) if (me.CsType == csname) return name; return csname; }