Skip to content

Commit

Permalink
checkpoint: camelCase dict keys; compact fields
Browse files Browse the repository at this point in the history
  • Loading branch information
lambdageek committed Apr 11, 2024
1 parent 2ad003d commit 355ac09
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 18 deletions.
69 changes: 67 additions & 2 deletions src/native/managed/cdacreader/src/ContractDescriptorParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.Diagnostics.DataContractReader;
public partial class ContractDescriptorParser
{
public const string TypeDescriptorSizeSigil = "!";

public static CompactContractDescriptor? Parse(ReadOnlySpan<byte> json)
{
return JsonSerializer.Deserialize(json, ContractDescriptorContext.Default.CompactContractDescriptor);
Expand All @@ -23,14 +25,18 @@ public partial class ContractDescriptorParser
[JsonSerializable(typeof(Dictionary<string, FieldDescriptor>))]
[JsonSerializable(typeof(TypeDescriptor))]
[JsonSerializable(typeof(FieldDescriptor))]
[JsonSourceGenerationOptions(AllowTrailingCommas = true,
DictionaryKeyPolicy = JsonKnownNamingPolicy.Unspecified, // contracts, types and globals are case sensitive
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
ReadCommentHandling = JsonCommentHandling.Skip)]
internal sealed partial class ContractDescriptorContext : JsonSerializerContext
{
}

// TODO: fix the key names to use lowercase
public class CompactContractDescriptor
{
public int Version { get; set; }
public int? Version { get; set; }
public string? Baseline { get; set; }
public Dictionary<string, int>? Contracts { get; set; }

Expand All @@ -50,6 +56,7 @@ public class TypeDescriptor
}

// TODO: compact format needs a custom converter
[JsonConverter(typeof(FieldDescriptorConverter))]
public class FieldDescriptor
{
public string? Type { get; set; }
Expand Down Expand Up @@ -104,4 +111,62 @@ public override void Write(Utf8JsonWriter writer, TypeDescriptor value, JsonSeri
}
}

internal sealed class FieldDescriptorConverter : JsonConverter<FieldDescriptor>
{
public override FieldDescriptor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number || reader.TokenType == JsonTokenType.String)
return new FieldDescriptor { Offset = reader.GetInt32() };
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
int eltIdx = 0;
string? type = null;
int offset = 0;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.EndArray:
return new FieldDescriptor { Type = type, Offset = offset };
case JsonTokenType.Comment:
// don't incrment eltIdx
continue;
default:
break;
}
switch (eltIdx)
{
case 0:
{
// expect an offset - either a string or a number token
if (reader.TokenType == JsonTokenType.Number || reader.TokenType == JsonTokenType.String)
offset = reader.GetInt32();
else
throw new JsonException();
break;
}
case 1:
{
// expect a type - a string token
if (reader.TokenType == JsonTokenType.String)
type = reader.GetString();
else
throw new JsonException();
break;
}
default:
// too many elements
throw new JsonException();
}
eltIdx++;
}
throw new JsonException();
}

public override void Write(Utf8JsonWriter writer, FieldDescriptor value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,105 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests;
public class ContractDescriptorParserTests
{


[Fact]
public void ParsesEmptyContract()
{
ReadOnlyMemory<byte> json = "{}"u8.ToArray();
ContractDescriptorParser.CompactContractDescriptor descriptor = ContractDescriptorParser.Parse(json.Span);
Assert.Null(descriptor.Version);
Assert.Null(descriptor.Baseline);
Assert.Null(descriptor.Contracts);
Assert.Null(descriptor.Types);
Assert.Null(descriptor.Extras);
}
[Fact]
public void ParsesTrivialContract()
{
ReadOnlyMemory<byte> json = """
{
"Version": 1,
"Baseline": "empty",
"Contracts": {},
"Types": {},
"Globals": {}
"version": 0,
"baseline": "empty",
"contracts": {},
"types": {},
"globals": {}
}
"""u8.ToArray();
ContractDescriptorParser.CompactContractDescriptor descriptor = ContractDescriptorParser.Parse(json.Span);
Assert.Equal(1, descriptor.Version);
Assert.Equal(0, descriptor.Version);
Assert.Equal("empty", descriptor.Baseline);
Assert.Empty(descriptor.Contracts);
Assert.Empty(descriptor.Types);
Assert.NotNull(descriptor.Extras["Globals"]);
Assert.NotNull(descriptor.Extras["globals"]);
}

[Fact]
public void ParseSizedTypes()
{
ReadOnlyMemory<byte> json = """
{
"Version": 1,
"Baseline": "empty",
"Contracts": {},
"Types":
"version": 0,
"baseline": "empty",
"contracts": {},
"types":
{
"pointer": { "!" : 8},
"int": { "!" : 4},
"Point": {
"x": { "Type": "int", "Offset": 4 },
"y": { "Type": "int", "Offset": 8 }
"x": [ 4, "int"],
"y": 8,
"!": 12
},
"Point3D": { // no size
"r": [ 0, "double"],
"phi": 8,
"rho": 16
}
}
}
"""u8.ToArray();
ContractDescriptorParser.CompactContractDescriptor descriptor = ContractDescriptorParser.Parse(json.Span);
Assert.Equal(1, descriptor.Version);
Assert.Equal(0, descriptor.Version);
Assert.Equal("empty", descriptor.Baseline);
Assert.Empty(descriptor.Contracts);
Assert.Equal(3, descriptor.Types.Count);
Assert.Equal(4, descriptor.Types.Count);
Assert.Equal(8u, descriptor.Types["pointer"].Size);
Assert.Equal(4u, descriptor.Types["int"].Size);
Assert.Equal(2, descriptor.Types["Point"].Fields.Count);
Assert.Equal(4, descriptor.Types["Point"].Fields["x"].Offset);
Assert.Equal(8, descriptor.Types["Point"].Fields["y"].Offset);
Assert.Equal(0u, descriptor.Types["Point"].Size);
Assert.Equal("int", descriptor.Types["Point"].Fields["x"].Type);
Assert.Null(descriptor.Types["Point"].Fields["y"].Type);
Assert.Equal(12u, descriptor.Types["Point"].Size);
Assert.Equal(3, descriptor.Types["Point3D"].Fields.Count);
Assert.Equal(0, descriptor.Types["Point3D"].Fields["r"].Offset);
Assert.Equal(8, descriptor.Types["Point3D"].Fields["phi"].Offset);
Assert.Equal(16, descriptor.Types["Point3D"].Fields["rho"].Offset);
Assert.Equal("double", descriptor.Types["Point3D"].Fields["r"].Type);
Assert.Null(descriptor.Types["Point3D"].Fields["phi"].Type);
Assert.Equal(0u, descriptor.Types["Point3D"].Size);
}

[Fact]
public void ParseContractsCaseSensitive()
{
ReadOnlyMemory<byte> json = """
{
"version": 0,
"baseline": "empty",
"contracts": {
"foo": 1,
"Foo": 2
},
"types": {},
"globals": {}
}
"""u8.ToArray();
ContractDescriptorParser.CompactContractDescriptor descriptor = ContractDescriptorParser.Parse(json.Span);
Assert.Equal(0, descriptor.Version);
Assert.Equal("empty", descriptor.Baseline);
Assert.Equal(2, descriptor.Contracts.Count);
Assert.Equal(1, descriptor.Contracts["foo"]);
Assert.Equal(2, descriptor.Contracts["Foo"]);
}
}

0 comments on commit 355ac09

Please sign in to comment.