Skip to content

Commit

Permalink
fix type values and name used for hash
Browse files Browse the repository at this point in the history
  • Loading branch information
vandonr committed Feb 18, 2025
1 parent 4c632a2 commit d45d715
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal interface IFieldDescriptorProxy

bool IsRepeated { get; }

int FieldType { get; } // actually an enum
ProtobufDotnetFieldType FieldType { get; }

int FieldNumber { get; }

Expand All @@ -59,6 +59,7 @@ internal interface IFieldDescriptorProxy
internal struct MessageDescriptorProxy
{
public string Name;
public string FullName;
public IFieldCollectionProxy Fields;

public IDescriptorProxy File;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// <copyright file="ProtobufTypes.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable

namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Protobuf;

/// <summary>
/// This represents protobuf types when they are handled by the C# code.
/// copy-pasted from https://github.com/protocolbuffers/protobuf/blob/main/csharp/src/Google.Protobuf/Reflection/FieldType.cs
/// </summary>
internal enum ProtobufDotnetFieldType
{
Double,
Float,
Int64,
UInt64,
Int32,
Fixed64,
Fixed32,
Bool,
String,
Group,
Message,
Bytes,
UInt32,
SFixed32,
SFixed64,
SInt32,
SInt64,
Enum
}

/// <summary>
/// This is the definition of types common to all languages.
/// copy pasted from https://github.com/protocolbuffers/protobuf/blob/78db3094a46e7a8cc34d207808b8008997b5fc82/csharp/src/Google.Protobuf/Reflection/Descriptor.pb.cs#L3962
/// </summary>
internal enum ProtobufDotnetProtoType
{
Double = 1,
Float = 2,
Int64 = 3,
Uint64 = 4,
Int32 = 5,
Fixed64 = 6,
Fixed32 = 7,
Bool = 8,
String = 9,
Group = 10,
Message = 11,
Bytes = 12,
Uint32 = 13,
Enum = 14,
Sfixed32 = 15,
Sfixed64 = 16,
Sint32 = 17,
Sint64 = 18,
Unknown = 999 // this one I added myself, to avoid using exceptions
}

/// <summary> Just a class to host the extension method. Must match the name of the file. </summary>
internal static class ProtobufTypes
{
/// <summary>
/// Converts from the dotnet-specific enum to the common enum.
/// this is the opposite of this function https://github.com/protocolbuffers/protobuf/blob/78db3094a46e7a8cc34d207808b8008997b5fc82/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs#L206
/// </summary>
public static ProtobufDotnetProtoType ToProtoType(this ProtobufDotnetFieldType type)
{
return type switch
{
ProtobufDotnetFieldType.Double => ProtobufDotnetProtoType.Double,
ProtobufDotnetFieldType.Float => ProtobufDotnetProtoType.Float,
ProtobufDotnetFieldType.Int64 => ProtobufDotnetProtoType.Int64,
ProtobufDotnetFieldType.UInt64 => ProtobufDotnetProtoType.Uint64,
ProtobufDotnetFieldType.Int32 => ProtobufDotnetProtoType.Int32,
ProtobufDotnetFieldType.Fixed64 => ProtobufDotnetProtoType.Fixed64,
ProtobufDotnetFieldType.Fixed32 => ProtobufDotnetProtoType.Fixed32,
ProtobufDotnetFieldType.Bool => ProtobufDotnetProtoType.Bool,
ProtobufDotnetFieldType.String => ProtobufDotnetProtoType.String,
ProtobufDotnetFieldType.Group => ProtobufDotnetProtoType.Group,
ProtobufDotnetFieldType.Message => ProtobufDotnetProtoType.Message,
ProtobufDotnetFieldType.Bytes => ProtobufDotnetProtoType.Bytes,
ProtobufDotnetFieldType.UInt32 => ProtobufDotnetProtoType.Uint32,
ProtobufDotnetFieldType.Enum => ProtobufDotnetProtoType.Enum,
ProtobufDotnetFieldType.SFixed32 => ProtobufDotnetProtoType.Sfixed32,
ProtobufDotnetFieldType.SFixed64 => ProtobufDotnetProtoType.Sfixed64,
ProtobufDotnetFieldType.SInt32 => ProtobufDotnetProtoType.Sint32,
ProtobufDotnetFieldType.SInt64 => ProtobufDotnetProtoType.Sint64,
_ => ProtobufDotnetProtoType.Unknown
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,78 +185,81 @@ private Dictionary<string, OpenApiSchema> ExtractFields(MessageDescriptorProxy d
string? type = null, format = null, description = null;
OpenApiReference? reference = null;
IList<IOpenApiAny>? enumValues = null;
switch (field.FieldType)

// the csharp implementation of protobuf uses an enum with different values to handle types internally.
// we must convert it back to the "common" value for consistency with other tracers
var protoType = field.FieldType.ToProtoType();
switch (protoType)
{
case 0:
case ProtobufDotnetProtoType.Double:
type = "number";
format = "double";
break;
case 1:
case ProtobufDotnetProtoType.Float:
type = "number";
format = "float";
break;
case 2:
case ProtobufDotnetProtoType.Int64:
type = "integer";
format = "int64";
break;
case 3:
case 16: // sint64
case ProtobufDotnetProtoType.Uint64:
case ProtobufDotnetProtoType.Sint64:
// OpenAPI does not directly support unsigned integers, treated as integers
type = "integer";
format = "uint64";
break;
case 4:
case 15: // sint32
case ProtobufDotnetProtoType.Int32:
case ProtobufDotnetProtoType.Sint32:
type = "integer";
format = "int32";
break;
case 5:
case ProtobufDotnetProtoType.Fixed64:
// Treated as an integer because OpenAPI does not have a fixed64 format.
type = "integer";
format = "fixed64";
break;
case 6:
case ProtobufDotnetProtoType.Fixed32:
type = "integer";
format = "fixed32";
break;
case 7:
case ProtobufDotnetProtoType.Bool:
type = "boolean";
break;
case 8:
case ProtobufDotnetProtoType.String:
type = "string";
break;
case 9: // group
case ProtobufDotnetProtoType.Group:
// Groups are deprecated and usually represented as nested messages in OpenAPI
type = "object";
description = "Group type";
break;
case 10: // message
case ProtobufDotnetProtoType.Message:
FillSchemasWith(field.MessageType, depth + 1); // Recursively add nested schemas (conditions apply)
reference = new OpenApiReference { Id = field.MessageType.Name, Type = ReferenceType.Schema };
_computedHash = FnvHash64.GenerateHash(reference.Id, FnvHash64.Version.V1A, _computedHash);
_computedHash = FnvHash64.GenerateHash(field.MessageType.FullName, FnvHash64.Version.V1A, _computedHash);
HashData.Append("|");
HashData.Append(reference.Id);
break;
case 11:
case ProtobufDotnetProtoType.Bytes:
type = "string";
format = "byte";
break;
case 12:
case ProtobufDotnetProtoType.Uint32:
// As with UINT64, treated as integers or strings because OpenAPI does not directly
// support unsigned integers
type = "integer";
format = "uint32";
break;
case 13:
case ProtobufDotnetProtoType.Sfixed32:
type = "integer";
format = "sfixed32";
break;
case 14:
case ProtobufDotnetProtoType.Sfixed64:
type = "integer";
format = "sfixed64";
break;
// cases 15 and 16 are above
case 17: // enum
case ProtobufDotnetProtoType.Enum:
type = "string";
enumValues = new List<IOpenApiAny>(field.EnumType.Values.Count);
foreach (var e in field.EnumType.Values)
Expand All @@ -278,12 +281,12 @@ private Dictionary<string, OpenApiSchema> ExtractFields(MessageDescriptorProxy d
}

_computedHash = FnvHash64.GenerateHash(field.FieldNumber.ToString(CultureInfo.InvariantCulture), FnvHash64.Version.V1A, _computedHash);
_computedHash = FnvHash64.GenerateHash(field.FieldType.ToString(CultureInfo.InvariantCulture), FnvHash64.Version.V1A, _computedHash);
_computedHash = FnvHash64.GenerateHash(((int)protoType).ToString(CultureInfo.InvariantCulture), FnvHash64.Version.V1A, _computedHash);
_computedHash = FnvHash64.GenerateHash(depth.ToString(CultureInfo.InvariantCulture), FnvHash64.Version.V1A, _computedHash);
HashData.Append("|");
HashData.Append(field.FieldNumber.ToString(CultureInfo.InvariantCulture));
HashData.Append("|");
HashData.Append(field.FieldType.ToString(CultureInfo.InvariantCulture));
HashData.Append(((int)protoType).ToString(CultureInfo.InvariantCulture));
HashData.Append("|");
HashData.Append(depth.ToString(CultureInfo.InvariantCulture));

Expand Down

0 comments on commit d45d715

Please sign in to comment.