Skip to content

precompute sizes and and simplify BasicProperties presence #890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 81 additions & 70 deletions projects/Apigen/apigen/Apigen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,43 +587,20 @@ public void EmitClassProperties(AmqpClass c)
}
EmitLine("");
foreach (AmqpField f in c.m_Fields)
{
if (!IsBoolean(f))
{
EmitLine($" private bool _{MangleMethod(f.Name)}_present = false;");
}
}
EmitLine("");
foreach (AmqpField f in c.m_Fields)
{
EmitSpecComment(f.DocumentationComment(" ", "@label"));
EmitLine($" public {maybeOverride}{MapDomain(f.Domain)} {MangleClass(f.Name)}");
EmitLine(" {");
EmitLine($" get => _{MangleMethod(f.Name)};");
EmitLine(" set");
EmitLine(" {");
if (!IsBoolean(f))
{
if (IsReferenceType(f))
{
EmitLine($" _{MangleMethod(f.Name)}_present = value != null;");

}
else
{
EmitLine($" _{MangleMethod(f.Name)}_present = true;");
}
}
EmitLine($" _{MangleMethod(f.Name)} = value;");
EmitLine(" }");
EmitLine($" set => _{MangleMethod(f.Name)} = value;");
EmitLine(" }");
EmitLine("");
}
foreach (AmqpField f in c.m_Fields)
{
if (!IsBoolean(f))
{
EmitLine($" public {maybeOverride}void Clear{MangleClass(f.Name)}() => _{MangleMethod(f.Name)}_present = false;");
EmitLine($" public {maybeOverride}void Clear{MangleClass(f.Name)}() => _{MangleMethod(f.Name)} = default;");
EmitLine("");
}
}
Expand All @@ -632,7 +609,7 @@ public void EmitClassProperties(AmqpClass c)
{
if (!IsBoolean(f))
{
EmitLine($" public {maybeOverride}bool Is{MangleClass(f.Name)}Present() => _{MangleMethod(f.Name)}_present;");
EmitLine($" public {maybeOverride}bool Is{MangleClass(f.Name)}Present() => _{MangleMethod(f.Name)} != default;");
EmitLine("");
}
}
Expand All @@ -647,19 +624,19 @@ public void EmitClassProperties(AmqpClass c)
{
if (IsBoolean(f))
{
EmitLine($" _{MangleMethod(f.Name)} = reader.ReadBit();");
EmitLine($" var {MangleMethod(f.Name)} = reader.ReadBit();");
}
else
{
EmitLine($" _{MangleMethod(f.Name)}_present = reader.ReadPresence();");
EmitLine($" var {MangleMethod(f.Name)}_present = reader.ReadPresence();");
}
}
EmitLine(" reader.FinishPresence();");
foreach (AmqpField f in c.m_Fields)
{
if (!IsBoolean(f))
{
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ _{MangleMethod(f.Name)} = reader.Read{MangleClass(ResolveDomain(f.Domain))}(); }}");
EmitLine($" if ({MangleMethod(f.Name)}_present) {{ _{MangleMethod(f.Name)} = reader.Read{MangleClass(ResolveDomain(f.Domain))}(); }}");
}
}
EmitLine(" }");
Expand All @@ -674,58 +651,56 @@ public void EmitClassProperties(AmqpClass c)
}
else
{
EmitLine($" writer.WritePresence(_{MangleMethod(f.Name)}_present);");
EmitLine($" writer.WritePresence(Is{MangleClass(f.Name)}Present());");
}
}
EmitLine(" writer.FinishPresence();");
foreach (AmqpField f in c.m_Fields)
{
if (!IsBoolean(f))
{
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ writer.Write{MangleClass(ResolveDomain(f.Domain))}(_{MangleMethod(f.Name)}); }}");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ writer.Write{MangleClass(ResolveDomain(f.Domain))}(_{MangleMethod(f.Name)}); }}");
}
}
EmitLine(" }");
EmitLine("");
EmitLine(" public override int GetRequiredPayloadBufferSize()");
EmitLine(" {");
EmitLine(" int bufferSize = 0;");
EmitLine(" int fieldCount = 0;");
EmitLine($" int bufferSize = {Math.Max((int)Math.Ceiling(c.m_Fields.Count / 15.0), 1) * 2}; // number of presence fields ({c.m_Fields.Count}) in 2 bytes blocks");
foreach (AmqpField f in c.m_Fields)
{
switch (MapDomain(f.Domain))
{
case "byte":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize++; }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize++; }} // _{MangleMethod(f.Name)} in bytes");
break;
case "string":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += 1 + Encoding.UTF8.GetByteCount(_{MangleMethod(f.Name)}); }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += 1 + Encoding.UTF8.GetByteCount(_{MangleMethod(f.Name)}); }} // _{MangleMethod(f.Name)} in bytes");
break;
case "byte[]":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += 4 + _{MangleMethod(f.Name)}.Length; }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += 4 + _{MangleMethod(f.Name)}.Length; }} // _{MangleMethod(f.Name)} in bytes");
break;
case "ushort":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += 2; }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += 2; }} // _{MangleMethod(f.Name)} in bytes");
break;
case "uint":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += 4; }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += 4; }} // _{MangleMethod(f.Name)} in bytes");
break;
case "ulong":
case "AmqpTimestamp":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += 8; }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += 8; }} // _{MangleMethod(f.Name)} in bytes");
break;
case "bool":
// TODO: implement if used, not used anywhere yet
break;
case "IDictionary<string, object>":
EmitLine($" if (_{MangleMethod(f.Name)}_present) {{ fieldCount++; bufferSize += WireFormatting.GetTableByteCount(_{MangleMethod(f.Name)}); }} // _{MangleMethod(f.Name)} in bytes");
EmitLine($" if (Is{MangleClass(f.Name)}Present()) {{ bufferSize += WireFormatting.GetTableByteCount(_{MangleMethod(f.Name)}); }} // _{MangleMethod(f.Name)} in bytes");
break;
default:
throw new ArgumentOutOfRangeException($"Can't handle size calculations for type = {f.Domain};");
}
}

EmitLine($" bufferSize += Math.Max((int)Math.Ceiling(fieldCount / 15.0), 1) * 2; // number of presence fields in bytes");
EmitLine(" return bufferSize;");
EmitLine(" }");
EmitLine("");
Expand All @@ -736,31 +711,23 @@ public void EmitClassProperties(AmqpClass c)
int remaining = c.m_Fields.Count;
foreach (AmqpField f in c.m_Fields)
{
Emit($" sb.Append(\"{f.Name}=\");");
Emit($" sb.Append(\"{f.Name}=\")");
if (IsBoolean(f))
{
Emit($" sb.Append(_{MangleMethod(f.Name)});");
Emit($".Append(_{MangleMethod(f.Name)})");
}
else
{
string x = MangleMethod(f.Name);
if (IsReferenceType(f))
{
Emit($" sb.Append(_{x}_present ? (_{x} == null ? \"(null)\" : _{x}.ToString()) : \"_\");");
}
else
{
Emit($" sb.Append(_{x}_present ? _{x}.ToString() : \"_\");");
}
Emit($".Append(Is{MangleClass(f.Name)}Present() ? _{MangleMethod(f.Name)}.ToString() : \"_\")");
}
remaining--;
if (remaining > 0)
{
EmitLine(" sb.Append(\", \");");
EmitLine(".Append(\", \");");
}
else
{
EmitLine("");
EmitLine(";");
}
}
}
Expand Down Expand Up @@ -817,7 +784,6 @@ public void EmitClassMethodImplementations(AmqpClass c)
EmitAutogeneratedSummary(" ", "Private implementation class - do not use directly.");
EmitLine($" internal sealed class {MangleMethodClass(c, m)} : Client.Impl.MethodBase, I{MangleMethodClass(c, m)}");
EmitLine(" {");
EmitLine("");
foreach (AmqpField f in m.m_Fields)
{
EmitLine($" public {MapDomain(f.Domain)} _{MangleMethod(f.Name)};");
Expand Down Expand Up @@ -882,69 +848,114 @@ public void EmitClassMethodImplementations(AmqpClass c)
EmitLine("");
EmitLine(" public override int GetRequiredBufferSize()");
EmitLine(" {");
EmitLine(" int bufferSize = 0;");

int bitCount = 0;
int bytesSize = 0;
var commentBuilder = new StringBuilder(" // bytes for ");
foreach (AmqpField f in m.m_Fields)
{
switch (MapDomain(f.Domain))
{
case "byte":
EmitLine($" bufferSize++; // _{MangleMethod(f.Name)} in bytes");
bytesSize++;
commentBuilder.Append('_').Append(MangleMethod(f.Name)).Append(", ");
break;
case "string":
EmitLine($" bufferSize += 1 + Encoding.UTF8.GetByteCount(_{MangleMethod(f.Name)}); // _{MangleMethod(f.Name)} in bytes");
bytesSize++;
commentBuilder.Append("length of _").Append(MangleMethod(f.Name)).Append(", ");
break;
case "byte[]":
EmitLine($" bufferSize += 4 + _{MangleMethod(f.Name)}.Length; // _{MangleMethod(f.Name)} in bytes");
bytesSize += 4;
commentBuilder.Append("length of _").Append(MangleMethod(f.Name)).Append(", ");
break;
case "ushort":
EmitLine($" bufferSize += 2; // _{MangleMethod(f.Name)} in bytes");
bytesSize += 2;
commentBuilder.Append('_').Append(MangleMethod(f.Name)).Append(", ");
break;
case "uint":
EmitLine($" bufferSize += 4; // _{MangleMethod(f.Name)} in bytes");
bytesSize += 4;
commentBuilder.Append('_').Append(MangleMethod(f.Name)).Append(", ");
break;
case "ulong":
case "AmqpTimestamp":
EmitLine($" bufferSize += 8; // _{MangleMethod(f.Name)} in bytes");
bytesSize += 8;
commentBuilder.Append('_').Append(MangleMethod(f.Name)).Append(", ");
break;
case "bool":
if (bitCount == 0)
{
commentBuilder.Append("bit fields, ");
}
bitCount++;
break;
case "IDictionary<string, object>":
EmitLine($" bufferSize += WireFormatting.GetTableByteCount(_{MangleMethod(f.Name)}); // _{MangleMethod(f.Name)} in bytes");
break;
default:
throw new ArgumentOutOfRangeException($"Can't handle size calculations for type = {f.Domain};");
}
}

if (bitCount > 0)
// 13 = " // bytes for "
if (commentBuilder.Length > 14)
{
// cut of last ", "
commentBuilder.Length -= 2;
}
else
{
EmitLine($" bufferSize += {Math.Ceiling(bitCount / 8.0)}; // number of bit fields in bytes");
commentBuilder.Clear();
}
bytesSize += (int)Math.Ceiling(bitCount / 8.0);
EmitLine($" int bufferSize = {bytesSize};{commentBuilder}");
foreach (AmqpField f in m.m_Fields)
{
switch (MapDomain(f.Domain))
{
case "byte":
case "ushort":
case "uint":
case "ulong":
case "AmqpTimestamp":
case "bool":
// size already calculated
break;
case "string":
EmitLine($" bufferSize += Encoding.UTF8.GetByteCount(_{MangleMethod(f.Name)}); // _{MangleMethod(f.Name)} in bytes");
break;
case "byte[]":
EmitLine($" bufferSize += _{MangleMethod(f.Name)}.Length; // _{MangleMethod(f.Name)} in bytes");
break;
case "IDictionary<string, object>":
EmitLine($" bufferSize += WireFormatting.GetTableByteCount(_{MangleMethod(f.Name)}); // _{MangleMethod(f.Name)} in bytes");
break;
default:
throw new ArgumentOutOfRangeException($"Can't handle size calculations for type = {f.Domain};");
}
}

EmitLine(" return bufferSize;");
EmitLine(" }");
EmitLine("");
EmitLine(" public override void AppendArgumentDebugStringTo(StringBuilder sb)");
EmitLine(" {");
EmitLine(" sb.Append(\"(\");");
EmitLine(" sb.Append('(');");
{
int remaining = m.m_Fields.Count;
foreach (AmqpField f in m.m_Fields)
{
Emit($" sb.Append(_{MangleMethod(f.Name)});");
Emit($" sb.Append(_{MangleMethod(f.Name)})");
remaining--;
if (remaining > 0)
{
EmitLine(" sb.Append(\",\");");
EmitLine(".Append(',');");
}
else
{
EmitLine("");
EmitLine(";");
}
}
}
EmitLine(" sb.Append(\")\");");
EmitLine(" sb.Append(')');");
EmitLine(" }");
EmitLine(" }");
}
Expand Down
14 changes: 13 additions & 1 deletion projects/RabbitMQ.Client/client/api/AmqpTimestamp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------

using System;

namespace RabbitMQ.Client
{
// time representations in mainstream languages: the horror, the horror
Expand All @@ -58,7 +60,7 @@ namespace RabbitMQ.Client
/// timestamps are signed or unsigned.
/// </para>
/// </remarks>
public struct AmqpTimestamp
public struct AmqpTimestamp : IEquatable<AmqpTimestamp>
{
/// <summary>
/// Construct an <see cref="AmqpTimestamp"/>.
Expand All @@ -74,6 +76,16 @@ public AmqpTimestamp(long unixTime) : this()
/// </summary>
public long UnixTime { get; private set; }

public bool Equals(AmqpTimestamp other) => UnixTime == other.UnixTime;

public override bool Equals(object obj) => obj is AmqpTimestamp other && Equals(other);

public override int GetHashCode() => UnixTime.GetHashCode();

public static bool operator ==(AmqpTimestamp left, AmqpTimestamp right) => left.Equals(right);

public static bool operator !=(AmqpTimestamp left, AmqpTimestamp right) => !left.Equals(right);

/// <summary>
/// Provides a debugger-friendly display.
/// </summary>
Expand Down