Skip to content
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

Performance improvements #2215

Merged
merged 10 commits into from
Jan 4, 2022
78 changes: 39 additions & 39 deletions src/Microsoft.OData.Core/InstanceAnnotationWriteTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,46 @@

namespace Microsoft.OData
{
using System;
using System.Collections.Generic;
using System;
using System.Collections.Generic;

/// <summary>
/// Helper class to track if an annotation has been written.
/// </summary>
internal sealed class InstanceAnnotationWriteTracker
{
/// <summary>
/// Maintains the write status for each annotation using its key.
/// If a key exists in the list then it is considered written.
/// </summary>
private readonly HashSet<string> writeStatus;
/// <summary>
/// Helper class to track if an annotation has been written.
/// </summary>
internal sealed class InstanceAnnotationWriteTracker
{
/// <summary>
/// Maintains the write status for each annotation using its key.
/// If a key exists in the list then it is considered written.
/// </summary>
private readonly HashSet<string> writeStatus;

/// <summary>
/// Creates a new <see cref="InstanceAnnotationWriteTracker"/> to hold write status for instance annotations.
/// </summary>
public InstanceAnnotationWriteTracker()
{
this.writeStatus = new HashSet<string>(StringComparer.Ordinal);
}
/// <summary>
/// Creates a new <see cref="InstanceAnnotationWriteTracker"/> to hold write status for instance annotations.
/// </summary>
public InstanceAnnotationWriteTracker()
{
this.writeStatus = new HashSet<string>(StringComparer.Ordinal);
}

/// <summary>
/// Check if an annotation is already written.
/// </summary>
/// <returns>true if the annotation is written; otherwise false.</returns>
/// <param name="key">The key of the element to check if its written.</param>
public bool IsAnnotationWritten(string key)
{
return this.writeStatus.Contains(key);
}
/// <summary>
/// Check if an annotation is already written.
/// </summary>
/// <returns>true if the annotation is written; otherwise false.</returns>
/// <param name="key">The key of the element to check if its written.</param>
public bool IsAnnotationWritten(string key)
{
return this.writeStatus.Contains(key);
}

/// <summary>
/// Mark an annotation as written.
/// </summary>
/// <returns>true if the annotation was unmarked before; otherwise false.</returns>
/// <param name="key">The key of the element to mark as written.</param>
public bool MarkAnnotationWritten(string key)
{
return this.writeStatus.Add(key);
}
}
}
/// <summary>
/// Mark an annotation as written.
/// </summary>
/// <returns>true if the annotation was unmarked before; otherwise false.</returns>
/// <param name="key">The key of the element to mark as written.</param>
public bool MarkAnnotationWritten(string key)
{
return this.writeStatus.Add(key);
}
}
}
52 changes: 25 additions & 27 deletions src/Microsoft.OData.Core/Json/JsonValueUtilsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal static partial class JsonValueUtils
/// <param name="writer">The text writer to write the output to.</param>
/// <param name="value">The char value to write.</param>
/// <param name="stringEscapeOption">The ODataStringEscapeOption to use in escaping the string.</param>
internal static async Task WriteValueAsync(this TextWriter writer, char value, ODataStringEscapeOption stringEscapeOption)
internal static Task WriteValueAsync(this TextWriter writer, char value, ODataStringEscapeOption stringEscapeOption)
{
Debug.Assert(writer != null, "writer != null");

Expand All @@ -39,12 +39,11 @@ internal static async Task WriteValueAsync(this TextWriter writer, char value, O
string escapedString = SpecialCharToEscapedStringMap[value];
if (escapedString != null)
{
await writer.WriteAsync(escapedString).ConfigureAwait(false);
return;
return writer.WriteAsync(escapedString);
}
}

await writer.WriteAsync(value).ConfigureAwait(false);
return writer.WriteAsync(value);
}

/// <summary>
Expand Down Expand Up @@ -122,27 +121,26 @@ internal static Task WriteValueAsync(this TextWriter writer, long value)
/// </summary>
/// <param name="writer">The text writer to write the output to.</param>
/// <param name="value">Double value to be written.</param>
internal static async Task WriteValueAsync(this TextWriter writer, double value)
internal static Task WriteValueAsync(this TextWriter writer, double value)
{
Debug.Assert(writer != null, "writer != null");

if (JsonSharedUtils.IsDoubleValueSerializedAsString(value))
{
await writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo)).ConfigureAwait(false);
return writer.WriteQuotedAsync(value.ToString(ODataNumberFormatInfo));
}
else
{
// double.ToString() supports a max scale of 14,
// whereas double.MinValue and double.MaxValue have 16 digits scale. Hence we need
// to use XmlConvert in all other cases, except infinity
string valueToWrite = XmlConvert.ToString(value);

await writer.WriteAsync(valueToWrite).ConfigureAwait(false);
if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0)
{
await writer.WriteAsync(".0").ConfigureAwait(false);
}
// double.ToString() supports a max scale of 14,
// whereas double.MinValue and double.MaxValue have 16 digits scale. Hence we need
// to use XmlConvert in all other cases, except infinity
var valueToWrite = XmlConvert.ToString(value);

if (valueToWrite.IndexOfAny(DoubleIndicatingCharacters) < 0)
{
valueToWrite += ".0";
}

return writer.WriteAsync(valueToWrite);
}

/// <summary>
Expand Down Expand Up @@ -176,7 +174,7 @@ internal static Task WriteValueAsync(this TextWriter writer, decimal value)
/// <param name="value">DateTimeOffset value to be written.</param>
/// <param name="dateTimeFormat">The format to write out the DateTime value in.</param>
[SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.OData.Json.JsonValueUtils.WriteQuotedAsync(System.IO.TextWriter,System.String)", Justification = "Constant defined by the JSON spec.")]
internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffset value, ODataJsonDateTimeFormat dateTimeFormat)
internal static Task WriteValueAsync(this TextWriter writer, DateTimeOffset value, ODataJsonDateTimeFormat dateTimeFormat)
{
Debug.Assert(writer != null, "writer != null");

Expand All @@ -192,11 +190,9 @@ internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffse
//
// offset = 4DIGIT
string textValue = XmlConvert.ToString(value);
await writer.WriteQuotedAsync(textValue).ConfigureAwait(false);
return writer.WriteQuotedAsync(textValue);
}

break;

case ODataJsonDateTimeFormat.ODataDateTime:
{
// Uses the same format as DateTime but with offset:
Expand All @@ -210,11 +206,11 @@ internal static async Task WriteValueAsync(this TextWriter writer, DateTimeOffse
// ticks = *DIGIT
// offset = 4DIGIT
string textValue = FormatDateTimeAsJsonTicksString(value);
await writer.WriteQuotedAsync(textValue).ConfigureAwait(false);
return writer.WriteQuotedAsync(textValue);
}

break;
}

return TaskUtils.CompletedTask;
}

/// <summary>
Expand Down Expand Up @@ -321,7 +317,7 @@ internal static async Task WriteValueAsync(this TextWriter writer, byte[] value,
}
else
{
await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false);
await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false);
await writer.WriteBinaryStringAsync(value, buffer, arrayPool).ConfigureAwait(false);
await writer.WriteAsync(JsonConstants.QuoteCharacter).ConfigureAwait(false);
}
Expand Down Expand Up @@ -451,7 +447,7 @@ internal static async Task WriteEscapedJsonStringValueAsync(
/// <param name="stringEscapeOption">The string escape option.</param>
/// <param name="buffer">Char buffer to use for streaming data.</param>
/// <param name="bufferPool">Character buffer pool.</param>
internal static async Task WriteEscapedCharArrayAsync(
internal static Task WriteEscapedCharArrayAsync(
this TextWriter writer,
char[] inputArray,
int inputArrayOffset,
Expand All @@ -468,8 +464,10 @@ internal static async Task WriteEscapedCharArrayAsync(
// write remaining bytes in buffer
if (bufferIndex > 0)
{
await writer.WriteAsync(buffer.Value, 0, bufferIndex).ConfigureAwait(false);
return writer.WriteAsync(buffer.Value, 0, bufferIndex);
}

return TaskUtils.CompletedTask;
}

/// <summary>
Expand Down
13 changes: 7 additions & 6 deletions src/Microsoft.OData.Core/Json/JsonWriterAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,25 @@ public Task EndTextWriterValueScopeAsync()
/// Asynchronously writes a separator of a value if it's needed for the next value to be written.
/// </summary>
/// <returns>A task that represents the asynchronous write operation.</returns>
private async Task WriteValueSeparatorAsync()
private Task WriteValueSeparatorAsync()
{
if (this.scopes.Count == 0)
{
return;
return TaskUtils.CompletedTask;
}

Scope currentScope = this.scopes.Peek();
if (currentScope.Type == ScopeType.Array)
{
if (currentScope.ObjectCount != 0)
currentScope.ObjectCount++;
if (currentScope.ObjectCount > 1)
{
await this.writer.WriteAsync(JsonConstants.ArrayElementSeparator)
.ConfigureAwait(false);
return this.writer.WriteAsync(JsonConstants.ArrayElementSeparator);
}

currentScope.ObjectCount++;

}
return TaskUtils.CompletedTask;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -916,21 +916,20 @@ await this.WriteStreamInfoAsync(propertyName, streamInfo)
/// <param name="isTopLevel">If writing top level property.</param>
/// <param name="isUndeclaredProperty">If writing an undeclared property.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
private async Task WriteInstanceAnnotationAsync(ODataPropertyInfo property, bool isTopLevel, bool isUndeclaredProperty)
private Task WriteInstanceAnnotationAsync(ODataPropertyInfo property, bool isTopLevel, bool isUndeclaredProperty)
{
if (property.InstanceAnnotations.Count != 0)
{
if (isTopLevel)
{
await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations)
.ConfigureAwait(false);
return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations);
}
else
{
await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty)
.ConfigureAwait(false);
return this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.InstanceAnnotations, property.Name, isUndeclaredProperty);
}
}
return TaskUtils.CompletedTask;
}

/// <summary>
Expand All @@ -939,7 +938,7 @@ await this.InstanceAnnotationWriter.WriteInstanceAnnotationsAsync(property.Insta
/// <param name="property">The property to handle.</param>
/// <param name="isTopLevel">If writing top level property.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
private async Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, bool isTopLevel)
private Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, bool isTopLevel)
{
if (property.TypeAnnotation != null && property.TypeAnnotation.TypeName != null)
{
Expand All @@ -952,16 +951,17 @@ private async Task WriteODataTypeAnnotationAsync(ODataPropertyInfo property, boo
{
if (isTopLevel)
{
await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName)
.ConfigureAwait(false);
return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeName);
}
else
{
await this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName)
.ConfigureAwait(false);
return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(property.Name, typeName);

}
}
}

return TaskUtils.CompletedTask;
}

/// <summary>
Expand Down Expand Up @@ -1162,23 +1162,23 @@ await this.JsonLightValueSerializer.WritePrimitiveValueAsync(primitiveValue.Valu
/// Asynchronously writes the type name on the wire.
/// </summary>
/// <returns>A task that represents the asynchronous write operation.</returns>
private async Task WritePropertyTypeNameAsync()
private Task WritePropertyTypeNameAsync()
{
string typeNameToWrite = this.currentPropertyInfo.TypeNameToWrite;
if (typeNameToWrite != null)
{
// We write the type name as an instance annotation (named "odata.type") for top-level properties, but as a property annotation (e.g., "[email protected]") if not top level.
if (this.currentPropertyInfo.IsTopLevel)
{
await this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite)
.ConfigureAwait(false);
return this.AsynchronousODataAnnotationWriter.WriteODataTypeInstanceAnnotationAsync(typeNameToWrite);
}
else
{
await this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(this.currentPropertyInfo.PropertyName, typeNameToWrite)
.ConfigureAwait(false);
return this.AsynchronousODataAnnotationWriter.WriteODataTypePropertyAnnotationAsync(this.currentPropertyInfo.PropertyName, typeNameToWrite);
}
}

return TaskUtils.CompletedTask;
}
}
}
27 changes: 16 additions & 11 deletions src/Microsoft.OData.Core/ODataAnnotatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@

namespace Microsoft.OData
{
/// <summary>
/// Base class for all annotatable types in OData library.
/// </summary>
/// <summary>
/// Base class for all annotatable types in OData library.
/// </summary>
#if ORCAS
[Serializable]
#endif
public abstract class ODataAnnotatable
{
/// <summary>
/// Collection of custom instance annotations.
/// </summary>
public abstract class ODataAnnotatable
{
/// <summary>
/// Collection of custom instance annotations.
/// </summary>
#if ORCAS
[NonSerialized]
#endif
private ICollection<ODataInstanceAnnotation> instanceAnnotations = new Collection<ODataInstanceAnnotation>();
private ICollection<ODataInstanceAnnotation> instanceAnnotations;

/// <summary>
/// The annotation for storing @odata.type.
Expand All @@ -38,8 +38,13 @@ public abstract class ODataAnnotatable
/// <returns>The custom instance annotations.</returns>
internal ICollection<ODataInstanceAnnotation> GetInstanceAnnotations()
{
Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null");
return this.instanceAnnotations;
//Debug.Assert(this.instanceAnnotations != null, "this.instanceAnnotations != null");
if (this.instanceAnnotations == null)
{
this.instanceAnnotations = new List<ODataInstanceAnnotation>();
}

return this.instanceAnnotations;
}

/// <summary>
Expand Down
Loading