Skip to content

Commit

Permalink
Merge pull request #1915 from microsoft/fix/performance-type-serializ…
Browse files Browse the repository at this point in the history
…ation

fix: multiple performance fixes for type serialization feat: adds to identifier mapping to non nullable enum
  • Loading branch information
baywet authored Nov 13, 2024
2 parents 17f36c6 + bd9622e commit 5fef51c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 56 deletions.
18 changes: 17 additions & 1 deletion src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,27 @@ namespace Microsoft.OpenApi.Extensions
/// </summary>
public static class OpenApiTypeMapper
{
#nullable enable
/// <summary>
/// Maps a JsonSchema data type to an identifier.
/// </summary>
/// <param name="schemaType"></param>
/// <returns></returns>
public static string ToIdentifier(this JsonSchemaType? schemaType)
public static string? ToIdentifier(this JsonSchemaType? schemaType)
{
if (schemaType is null)
{
return null;
}
return schemaType.Value.ToIdentifier();
}

/// <summary>
/// Maps a JsonSchema data type to an identifier.
/// </summary>
/// <param name="schemaType"></param>
/// <returns></returns>
public static string? ToIdentifier(this JsonSchemaType schemaType)
{
return schemaType switch
{
Expand All @@ -32,6 +47,7 @@ public static string ToIdentifier(this JsonSchemaType? schemaType)
_ => null,
};
}
#nullable restore

/// <summary>
/// Converts a schema type's identifier into the enum equivalent
Expand Down
96 changes: 42 additions & 54 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -476,10 +476,7 @@ public void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
writer.WriteOptionalCollection(OpenApiConstants.Enum, Enum, (nodeWriter, s) => nodeWriter.WriteAny(s));

// type
if (Type is not null)
{
SerializeTypeProperty(Type, writer, version);
}
SerializeTypeProperty(Type, writer, version);

// allOf
writer.WriteOptionalCollection(OpenApiConstants.AllOf, AllOf, callback);
Expand Down Expand Up @@ -660,10 +657,7 @@ internal void SerializeAsV2(
writer.WriteStartObject();

// type
if (Type is not null)
{
SerializeTypeProperty(Type, writer, OpenApiSpecVersion.OpenApi2_0);
}
SerializeTypeProperty(Type, writer, OpenApiSpecVersion.OpenApi2_0);

// description
writer.WriteProperty(OpenApiConstants.Description, Description);
Expand Down Expand Up @@ -794,8 +788,11 @@ internal void SerializeAsV2(

private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer, OpenApiSpecVersion version)
{
var flagsCount = CountEnumSetFlags(type);
if (flagsCount is 1)
if (type is null)
{
return;
}
if (!HasMultipleTypes(type.Value))
{
// check whether nullable is true for upcasting purposes
if (version is OpenApiSpecVersion.OpenApi3_1 && (Nullable || Extensions.ContainsKey(OpenApiConstants.NullableExtension)))
Expand All @@ -804,61 +801,50 @@ private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer,
}
else
{
writer.WriteProperty(OpenApiConstants.Type, type.ToIdentifier());
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
}
}
else if(flagsCount > 1)
else
{
// type
if (version is OpenApiSpecVersion.OpenApi2_0 || version is OpenApiSpecVersion.OpenApi3_0)
{
DowncastTypeArrayToV2OrV3(type, writer, version, flagsCount);
DowncastTypeArrayToV2OrV3(type.Value, writer, version);
}
else
{
if (type is not null)
var list = new List<JsonSchemaType>();
foreach (JsonSchemaType flag in jsonSchemaTypeValues)
{
var list = new List<JsonSchemaType?>();
foreach (JsonSchemaType flag in System.Enum.GetValues(typeof(JsonSchemaType)))
if (type.Value.HasFlag(flag))
{
if (type.Value.HasFlag(flag))
{
list.Add(flag);
}
list.Add(flag);
}
}

writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier()));
}
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier()));
}
}
}

private static int CountEnumSetFlags(JsonSchemaType? schemaType)
private static bool IsPowerOfTwo(int x)
{
int count = 0;

if (schemaType != null)
{
// Check each flag in the enum
foreach (JsonSchemaType value in System.Enum.GetValues(typeof(JsonSchemaType)))
{
// Check if the flag is set
if (schemaType.Value.HasFlag(value))
{
count++;
}
}
}
return x != 0 && (x & (x - 1)) == 0;
}

return count;
private static bool HasMultipleTypes(JsonSchemaType schemaType)
{
var schemaTypeNumeric = (int)schemaType;
return !IsPowerOfTwo(schemaTypeNumeric) && // Boolean, Integer, Number, String, Array, Object
schemaTypeNumeric != (int)JsonSchemaType.Null;
}

private void UpCastSchemaTypeToV31(JsonSchemaType? type, IOpenApiWriter writer)
{
// create a new array and insert the type and "null" as values
Type = type | JsonSchemaType.Null;
var list = new List<string>();
foreach (JsonSchemaType? flag in System.Enum.GetValues(typeof(JsonSchemaType)))
foreach (JsonSchemaType? flag in jsonSchemaTypeValues)
{
// Check if the flag is set in 'type' using a bitwise AND operation
if (Type.Value.HasFlag(flag))
Expand All @@ -870,7 +856,9 @@ private void UpCastSchemaTypeToV31(JsonSchemaType? type, IOpenApiWriter writer)
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s));
}

private void DowncastTypeArrayToV2OrV3(JsonSchemaType? schemaType, IOpenApiWriter writer, OpenApiSpecVersion version, int flagsCount)
private static readonly Array jsonSchemaTypeValues = System.Enum.GetValues(typeof(JsonSchemaType));

private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter writer, OpenApiSpecVersion version)
{
/* If the array has one non-null value, emit Type as string
* If the array has one null value, emit x-nullable as true
Expand All @@ -882,23 +870,12 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType? schemaType, IOpenApiWrite
? OpenApiConstants.NullableExtension
: OpenApiConstants.Nullable;

if (flagsCount is 1)
if (!HasMultipleTypes(schemaType ^ JsonSchemaType.Null) && (schemaType & JsonSchemaType.Null) == JsonSchemaType.Null) // checks for two values and one is null
{
if (schemaType is JsonSchemaType.Null)
{
writer.WriteProperty(nullableProp, true);
}
else
{
writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier());
}
}
else if (flagsCount is 2 && (schemaType & JsonSchemaType.Null) == JsonSchemaType.Null) // checks for two values and one is null
{
foreach (JsonSchemaType? flag in System.Enum.GetValues(typeof(JsonSchemaType)))
foreach (JsonSchemaType? flag in jsonSchemaTypeValues)
{
// Skip if the flag is not set or if it's the Null flag
if (schemaType.Value.HasFlag(flag) && flag != JsonSchemaType.Null)
if (schemaType.HasFlag(flag) && flag != JsonSchemaType.Null)
{
// Write the non-null flag value to the writer
writer.WriteProperty(OpenApiConstants.Type, flag.ToIdentifier());
Expand All @@ -909,6 +886,17 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType? schemaType, IOpenApiWrite
writer.WriteProperty(nullableProp, true);
}
}
else if (!HasMultipleTypes(schemaType))
{
if (schemaType is JsonSchemaType.Null)
{
writer.WriteProperty(nullableProp, true);
}
else
{
writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier());
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ namespace Microsoft.OpenApi.Extensions
{
public static System.Type MapOpenApiPrimitiveTypeToSimpleType(this Microsoft.OpenApi.Models.OpenApiSchema schema) { }
public static Microsoft.OpenApi.Models.OpenApiSchema MapTypeToOpenApiPrimitiveType(this System.Type type) { }
public static string ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType? schemaType) { }
public static string? ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { }
public static string? ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType? schemaType) { }
public static Microsoft.OpenApi.Models.JsonSchemaType ToJsonSchemaType(this string identifier) { }
}
public static class StringExtensions
Expand Down

0 comments on commit 5fef51c

Please sign in to comment.