From 3462ac96cba76d414136771723d6a24088484853 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 26 Mar 2024 13:21:58 -0700 Subject: [PATCH 1/4] Add TagWriter implementation. --- OpenTelemetry.sln | 8 + src/Shared/TagWriter/ArrayTagWriter.cs | 24 ++ .../TagWriter/JsonStringArrayTagWriter.cs | 99 ++++++ src/Shared/TagWriter/TagWriter.cs | 301 ++++++++++++++++++ 4 files changed, 432 insertions(+) create mode 100644 src/Shared/TagWriter/ArrayTagWriter.cs create mode 100644 src/Shared/TagWriter/JsonStringArrayTagWriter.cs create mode 100644 src/Shared/TagWriter/TagWriter.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 81139695639..754e173c437 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -347,6 +347,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configurat src\Shared\Configuration\OpenTelemetryConfigurationExtensions.cs = src\Shared\Configuration\OpenTelemetryConfigurationExtensions.cs EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TagWriter", "TagWriter", "{993E65E5-E71B-40FD-871C-60A9EBD59724}" + ProjectSection(SolutionItems) = preProject + src\Shared\TagWriter\ArrayTagWriter.cs = src\Shared\TagWriter\ArrayTagWriter.cs + src\Shared\TagWriter\JsonStringArrayTagWriter.cs = src\Shared\TagWriter\JsonStringArrayTagWriter.cs + src\Shared\TagWriter\TagWriter.cs = src\Shared\TagWriter\TagWriter.cs + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -693,6 +700,7 @@ Global {7BE494FC-4B0D-4340-A62A-9C9F3E7389FE} = {A115CE4C-71A8-4B95-96A5-C1DF46FD94C2} {19545B37-8518-4BDD-AD49-00C031FB3C2A} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} {87A20A76-D524-4AAC-AF92-8725BFED0415} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} + {993E65E5-E71B-40FD-871C-60A9EBD59724} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/src/Shared/TagWriter/ArrayTagWriter.cs b/src/Shared/TagWriter/ArrayTagWriter.cs new file mode 100644 index 00000000000..3e9e69ef799 --- /dev/null +++ b/src/Shared/TagWriter/ArrayTagWriter.cs @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +namespace OpenTelemetry.Internal; + +internal abstract class ArrayTagWriter + where T : notnull +{ + public abstract T BeginWriteArray(); + + public abstract void WriteNullTag(T state); + + public abstract void WriteIntegralTag(T state, long value); + + public abstract void WriteFloatingPointTag(T state, double value); + + public abstract void WriteBooleanTag(T state, bool value); + + public abstract void WriteStringTag(T state, string value); + + public abstract void EndWriteArray(T state); +} diff --git a/src/Shared/TagWriter/JsonStringArrayTagWriter.cs b/src/Shared/TagWriter/JsonStringArrayTagWriter.cs new file mode 100644 index 00000000000..416196243ad --- /dev/null +++ b/src/Shared/TagWriter/JsonStringArrayTagWriter.cs @@ -0,0 +1,99 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics; +using System.Text.Json; + +namespace OpenTelemetry.Internal; + +internal abstract class JsonStringArrayTagWriter : TagWriter.JsonArrayTagWriterState> + where TTagState : notnull +{ + protected JsonStringArrayTagWriter() + : base(new JsonArrayTagWriter()) + { + } + + protected sealed override void WriteArrayTag(TTagState writer, string key, JsonStringArrayTagWriter.JsonArrayTagWriterState array) + { + var result = array.Stream.TryGetBuffer(out var buffer); + + Debug.Assert(result, "result was false"); + + this.WriteArrayTag(writer, key, buffer); + } + + protected abstract void WriteArrayTag(TTagState writer, string key, ArraySegment arrayUtf8JsonBytes); + + internal readonly struct JsonArrayTagWriterState(MemoryStream stream, Utf8JsonWriter writer) + { + public MemoryStream Stream { get; } = stream; + + public Utf8JsonWriter Writer { get; } = writer; + } + + internal sealed class JsonArrayTagWriter : ArrayTagWriter + { + [ThreadStatic] + private static MemoryStream? threadStream; + + [ThreadStatic] + private static Utf8JsonWriter? threadWriter; + + public override JsonArrayTagWriterState BeginWriteArray() + { + var state = EnsureWriter(); + state.Writer.WriteStartArray(); + return state; + } + + public override void EndWriteArray(JsonArrayTagWriterState state) + { + state.Writer.WriteEndArray(); + state.Writer.Flush(); + } + + public override void WriteBooleanTag(JsonArrayTagWriterState state, bool value) + { + state.Writer.WriteBooleanValue(value); + } + + public override void WriteFloatingPointTag(JsonArrayTagWriterState state, double value) + { + state.Writer.WriteNumberValue(value); + } + + public override void WriteIntegralTag(JsonArrayTagWriterState state, long value) + { + state.Writer.WriteNumberValue(value); + } + + public override void WriteNullTag(JsonArrayTagWriterState state) + { + state.Writer.WriteNullValue(); + } + + public override void WriteStringTag(JsonArrayTagWriterState state, string value) + { + state.Writer.WriteStringValue(value); + } + + private static JsonArrayTagWriterState EnsureWriter() + { + if (threadStream == null) + { + threadStream = new MemoryStream(); + threadWriter = new Utf8JsonWriter(threadStream); + return new(threadStream, threadWriter); + } + else + { + threadStream.SetLength(0); + threadWriter!.Reset(threadStream); + return new(threadStream, threadWriter); + } + } + } +} diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs new file mode 100644 index 00000000000..bf4415fba89 --- /dev/null +++ b/src/Shared/TagWriter/TagWriter.cs @@ -0,0 +1,301 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Buffers; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace OpenTelemetry.Internal; + +internal abstract class TagWriter + where TTagState : notnull + where TArrayState : notnull +{ + private readonly ArrayTagWriter arrayWriter; + + protected TagWriter( + ArrayTagWriter arrayTagWriter) + { + Guard.ThrowIfNull(arrayTagWriter); + + this.arrayWriter = arrayTagWriter; + } + + public bool TryWriteTag( + TTagState state, + KeyValuePair tag, + int? tagValueMaxLength = null) + { + if (tag.Value == null) + { + return false; + } + + switch (tag.Value) + { + case char: + case string: + this.WriteStringTag(state, tag.Key, TruncateString(Convert.ToString(tag.Value)!, tagValueMaxLength)); + break; + case bool b: + this.WriteBooleanTag(state, tag.Key, b); + break; + case byte: + case sbyte: + case short: + case ushort: + case int: + case uint: + case long: + this.WriteIntegralTag(state, tag.Key, Convert.ToInt64(tag.Value)); + break; + case float: + case double: + this.WriteFloatingPointTag(state, tag.Key, Convert.ToDouble(tag.Value)); + break; + case Array array: + try + { + this.WriteArrayTagInternal(state, tag.Key, array, tagValueMaxLength); + } + catch + { + // If an exception is thrown when calling ToString + // on any element of the array, then the entire array value + // is ignored. + return this.LogUnsupportedTagTypeAndReturnFalse(tag.Key, tag.Value); + } + + break; + + // All other types are converted to strings including the following + // built-in value types: + // case nint: Pointer type. + // case nuint: Pointer type. + // case ulong: May throw an exception on overflow. + // case decimal: Converting to double produces rounding errors. + default: + try + { + var stringValue = TruncateString(Convert.ToString(tag.Value, CultureInfo.InvariantCulture), tagValueMaxLength); + if (stringValue == null) + { + return this.LogUnsupportedTagTypeAndReturnFalse(tag.Key, tag.Value); + } + + this.WriteStringTag(state, tag.Key, stringValue); + } + catch + { + // If ToString throws an exception then the tag is ignored. + return this.LogUnsupportedTagTypeAndReturnFalse(tag.Key, tag.Value); + } + + break; + } + + return true; + } + + protected abstract void WriteIntegralTag(TTagState state, string key, long value); + + protected abstract void WriteFloatingPointTag(TTagState state, string key, double value); + + protected abstract void WriteBooleanTag(TTagState state, string key, bool value); + + protected abstract void WriteStringTag(TTagState state, string key, string value); + + protected abstract void WriteArrayTag(TTagState state, string key, TArrayState value); + + protected abstract void OnUnsupportedTagDropped( + string tagKey, + string tagValueTypeFullName); + + [return: NotNullIfNotNull(nameof(value))] + private static string? TruncateString(string? value, int? maxLength) + { + return maxLength.HasValue && value?.Length > maxLength + ? value.Substring(0, maxLength.Value) + : value; + } + + private void WriteArrayTagInternal(TTagState state, string key, Array array, int? tagValueMaxLength) + { + var arrayState = this.arrayWriter.BeginWriteArray(); + + // This switch ensures the values of the resultant array-valued tag are of the same type. + switch (array) + { + case char[] charArray: this.WriteToArray(arrayState, charArray); break; + case string[]: this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); break; + case bool[] boolArray: this.WriteToArray(arrayState, boolArray); break; + case byte[] byteArray: this.WriteToArray(arrayState, byteArray); break; + case sbyte[] sbyteArray: this.WriteToArray(arrayState, sbyteArray); break; + case short[] shortArray: this.WriteToArray(arrayState, shortArray); break; + case ushort[] ushortArray: this.WriteToArray(arrayState, ushortArray); break; +#if NETFRAMEWORK + case int[]: this.WriteArrayTagIntNetFramework(arrayState, array, tagValueMaxLength); break; +#else + case int[] intArray: this.WriteToArray(arrayState, intArray); break; +#endif + case uint[] uintArray: this.WriteToArray(arrayState, uintArray); break; +#if NETFRAMEWORK + case long[]: this.WriteArrayTagLongNetFramework(arrayState, array, tagValueMaxLength); break; +#else + case long[] longArray: this.WriteToArray(arrayState, longArray); break; +#endif + case float[] floatArray: this.WriteToArray(arrayState, floatArray); break; + case double[] doubleArray: this.WriteToArray(arrayState, doubleArray); break; + default: this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); break; + } + + this.arrayWriter.EndWriteArray(arrayState); + + this.WriteArrayTag(state, key, arrayState); + } + +#if NETFRAMEWORK + private void WriteArrayTagIntNetFramework(TArrayState arrayState, Array array, int? tagValueMaxLength) + { + // Note: On .NET Framework x86 nint[] & nuint[] fall into int[] case + + var arrayType = array.GetType(); + if (arrayType == typeof(nint[]) + || arrayType == typeof(nuint[])) + { + this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); + return; + } + + this.WriteToArray(arrayState, (int[])array); + } + + private void WriteArrayTagLongNetFramework(TArrayState arrayState, Array array, int? tagValueMaxLength) + { + // Note: On .NET Framework x64 nint[] & nuint[] fall into long[] case + + var arrayType = array.GetType(); + if (arrayType == typeof(nint[]) + || arrayType == typeof(nuint[])) + { + this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); + return; + } + + this.WriteToArray(arrayState, (long[])array); + } +#endif + + private void ConvertToStringArrayThenWriteArrayTag(TArrayState arrayState, Array array, int? tagValueMaxLength) + { + if (array is string?[] arrayAsStringArray + && (!tagValueMaxLength.HasValue || !arrayAsStringArray.Any(s => s?.Length > tagValueMaxLength))) + { + this.WriteStringsToArray(arrayState, arrayAsStringArray); + } + else + { + string?[] stringArray = ArrayPool.Shared.Rent(array.Length); + try + { + for (var i = 0; i < array.Length; ++i) + { + var item = array.GetValue(i); + stringArray[i] = item == null + ? null + : TruncateString(Convert.ToString(item, CultureInfo.InvariantCulture), tagValueMaxLength); + } + + this.WriteStringsToArray(arrayState, new(stringArray, 0, array.Length)); + } + finally + { + ArrayPool.Shared.Return(stringArray); + } + } + } + + private void WriteToArray(TArrayState arrayState, TItem[] array) + where TItem : struct + { + foreach (TItem item in array) + { + if (typeof(TItem) == typeof(char)) + { + this.arrayWriter.WriteStringTag(arrayState, Convert.ToString((char)(object)item)!); + } + else if (typeof(TItem) == typeof(bool)) + { + this.arrayWriter.WriteBooleanTag(arrayState, (bool)(object)item); + } + else if (typeof(TItem) == typeof(byte)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (byte)(object)item); + } + else if (typeof(TItem) == typeof(sbyte)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (sbyte)(object)item); + } + else if (typeof(TItem) == typeof(short)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (short)(object)item); + } + else if (typeof(TItem) == typeof(ushort)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (ushort)(object)item); + } + else if (typeof(TItem) == typeof(int)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (int)(object)item); + } + else if (typeof(TItem) == typeof(uint)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (uint)(object)item); + } + else if (typeof(TItem) == typeof(long)) + { + this.arrayWriter.WriteIntegralTag(arrayState, (long)(object)item); + } + else if (typeof(TItem) == typeof(float)) + { + this.arrayWriter.WriteFloatingPointTag(arrayState, (float)(object)item); + } + else if (typeof(TItem) == typeof(double)) + { + this.arrayWriter.WriteFloatingPointTag(arrayState, (double)(object)item); + } + else + { + Debug.Fail("Unexpected type encountered"); + + throw new NotSupportedException(); + } + } + } + + private void WriteStringsToArray(TArrayState arrayState, ReadOnlySpan data) + { + foreach (var item in data) + { + if (item == null) + { + this.arrayWriter.WriteNullTag(arrayState); + } + else + { + this.arrayWriter.WriteStringTag(arrayState, item); + } + } + } + + private bool LogUnsupportedTagTypeAndReturnFalse(string key, object value) + { + Debug.Assert(value != null, "value was null"); + + this.OnUnsupportedTagDropped(key, value!.GetType().ToString()); + return false; + } +} From 26ae16ffb84884cf417120879481efcd2eb2d636 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 8 Apr 2024 10:05:46 -0700 Subject: [PATCH 2/4] Code review. --- src/Shared/TagWriter/TagWriter.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs index bf4415fba89..58811cfd1c3 100644 --- a/src/Shared/TagWriter/TagWriter.cs +++ b/src/Shared/TagWriter/TagWriter.cs @@ -136,15 +136,12 @@ private void WriteArrayTagInternal(TTagState state, string key, Array array, int case sbyte[] sbyteArray: this.WriteToArray(arrayState, sbyteArray); break; case short[] shortArray: this.WriteToArray(arrayState, shortArray); break; case ushort[] ushortArray: this.WriteToArray(arrayState, ushortArray); break; -#if NETFRAMEWORK - case int[]: this.WriteArrayTagIntNetFramework(arrayState, array, tagValueMaxLength); break; -#else - case int[] intArray: this.WriteToArray(arrayState, intArray); break; -#endif case uint[] uintArray: this.WriteToArray(arrayState, uintArray); break; #if NETFRAMEWORK + case int[]: this.WriteArrayTagIntNetFramework(arrayState, array, tagValueMaxLength); break; case long[]: this.WriteArrayTagLongNetFramework(arrayState, array, tagValueMaxLength); break; #else + case int[] intArray: this.WriteToArray(arrayState, intArray); break; case long[] longArray: this.WriteToArray(arrayState, longArray); break; #endif case float[] floatArray: this.WriteToArray(arrayState, floatArray); break; From cb4055cb6ac12e042be101eacd11f0d1ee81769d Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 8 Apr 2024 12:20:03 -0700 Subject: [PATCH 3/4] Tweaked names and pass things by ref. --- src/Shared/TagWriter/ArrayTagWriter.cs | 18 +-- .../TagWriter/JsonStringArrayTagWriter.cs | 18 +-- src/Shared/TagWriter/TagWriter.cs | 111 +++++++++--------- 3 files changed, 75 insertions(+), 72 deletions(-) diff --git a/src/Shared/TagWriter/ArrayTagWriter.cs b/src/Shared/TagWriter/ArrayTagWriter.cs index 3e9e69ef799..b5eec4e2f0c 100644 --- a/src/Shared/TagWriter/ArrayTagWriter.cs +++ b/src/Shared/TagWriter/ArrayTagWriter.cs @@ -5,20 +5,20 @@ namespace OpenTelemetry.Internal; -internal abstract class ArrayTagWriter - where T : notnull +internal abstract class ArrayTagWriter + where TArrayState : notnull { - public abstract T BeginWriteArray(); + public abstract TArrayState BeginWriteArray(); - public abstract void WriteNullTag(T state); + public abstract void WriteNullValue(ref TArrayState state); - public abstract void WriteIntegralTag(T state, long value); + public abstract void WriteIntegralValue(ref TArrayState state, long value); - public abstract void WriteFloatingPointTag(T state, double value); + public abstract void WriteFloatingPointValue(ref TArrayState state, double value); - public abstract void WriteBooleanTag(T state, bool value); + public abstract void WriteBooleanValue(ref TArrayState state, bool value); - public abstract void WriteStringTag(T state, string value); + public abstract void WriteStringValue(ref TArrayState state, string value); - public abstract void EndWriteArray(T state); + public abstract void EndWriteArray(ref TArrayState state); } diff --git a/src/Shared/TagWriter/JsonStringArrayTagWriter.cs b/src/Shared/TagWriter/JsonStringArrayTagWriter.cs index 416196243ad..21addd5ba5b 100644 --- a/src/Shared/TagWriter/JsonStringArrayTagWriter.cs +++ b/src/Shared/TagWriter/JsonStringArrayTagWriter.cs @@ -16,16 +16,16 @@ protected JsonStringArrayTagWriter() { } - protected sealed override void WriteArrayTag(TTagState writer, string key, JsonStringArrayTagWriter.JsonArrayTagWriterState array) + protected sealed override void WriteArrayTag(ref TTagState writer, string key, ref JsonArrayTagWriterState array) { var result = array.Stream.TryGetBuffer(out var buffer); Debug.Assert(result, "result was false"); - this.WriteArrayTag(writer, key, buffer); + this.WriteArrayTag(ref writer, key, buffer); } - protected abstract void WriteArrayTag(TTagState writer, string key, ArraySegment arrayUtf8JsonBytes); + protected abstract void WriteArrayTag(ref TTagState writer, string key, ArraySegment arrayUtf8JsonBytes); internal readonly struct JsonArrayTagWriterState(MemoryStream stream, Utf8JsonWriter writer) { @@ -49,33 +49,33 @@ public override JsonArrayTagWriterState BeginWriteArray() return state; } - public override void EndWriteArray(JsonArrayTagWriterState state) + public override void EndWriteArray(ref JsonArrayTagWriterState state) { state.Writer.WriteEndArray(); state.Writer.Flush(); } - public override void WriteBooleanTag(JsonArrayTagWriterState state, bool value) + public override void WriteBooleanValue(ref JsonArrayTagWriterState state, bool value) { state.Writer.WriteBooleanValue(value); } - public override void WriteFloatingPointTag(JsonArrayTagWriterState state, double value) + public override void WriteFloatingPointValue(ref JsonArrayTagWriterState state, double value) { state.Writer.WriteNumberValue(value); } - public override void WriteIntegralTag(JsonArrayTagWriterState state, long value) + public override void WriteIntegralValue(ref JsonArrayTagWriterState state, long value) { state.Writer.WriteNumberValue(value); } - public override void WriteNullTag(JsonArrayTagWriterState state) + public override void WriteNullValue(ref JsonArrayTagWriterState state) { state.Writer.WriteNullValue(); } - public override void WriteStringTag(JsonArrayTagWriterState state, string value) + public override void WriteStringValue(ref JsonArrayTagWriterState state, string value) { state.Writer.WriteStringValue(value); } diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs index 58811cfd1c3..a8a749534eb 100644 --- a/src/Shared/TagWriter/TagWriter.cs +++ b/src/Shared/TagWriter/TagWriter.cs @@ -25,7 +25,7 @@ protected TagWriter( } public bool TryWriteTag( - TTagState state, + ref TTagState state, KeyValuePair tag, int? tagValueMaxLength = null) { @@ -38,10 +38,10 @@ public bool TryWriteTag( { case char: case string: - this.WriteStringTag(state, tag.Key, TruncateString(Convert.ToString(tag.Value)!, tagValueMaxLength)); + this.WriteStringTag(ref state, tag.Key, TruncateString(Convert.ToString(tag.Value)!, tagValueMaxLength)); break; case bool b: - this.WriteBooleanTag(state, tag.Key, b); + this.WriteBooleanTag(ref state, tag.Key, b); break; case byte: case sbyte: @@ -50,16 +50,16 @@ public bool TryWriteTag( case int: case uint: case long: - this.WriteIntegralTag(state, tag.Key, Convert.ToInt64(tag.Value)); + this.WriteIntegralTag(ref state, tag.Key, Convert.ToInt64(tag.Value)); break; case float: case double: - this.WriteFloatingPointTag(state, tag.Key, Convert.ToDouble(tag.Value)); + this.WriteFloatingPointTag(ref state, tag.Key, Convert.ToDouble(tag.Value)); break; case Array array: try { - this.WriteArrayTagInternal(state, tag.Key, array, tagValueMaxLength); + this.WriteArrayTagInternal(ref state, tag.Key, array, tagValueMaxLength); } catch { @@ -86,7 +86,7 @@ public bool TryWriteTag( return this.LogUnsupportedTagTypeAndReturnFalse(tag.Key, tag.Value); } - this.WriteStringTag(state, tag.Key, stringValue); + this.WriteStringTag(ref state, tag.Key, stringValue); } catch { @@ -100,15 +100,15 @@ public bool TryWriteTag( return true; } - protected abstract void WriteIntegralTag(TTagState state, string key, long value); + protected abstract void WriteIntegralTag(ref TTagState state, string key, long value); - protected abstract void WriteFloatingPointTag(TTagState state, string key, double value); + protected abstract void WriteFloatingPointTag(ref TTagState state, string key, double value); - protected abstract void WriteBooleanTag(TTagState state, string key, bool value); + protected abstract void WriteBooleanTag(ref TTagState state, string key, bool value); - protected abstract void WriteStringTag(TTagState state, string key, string value); + protected abstract void WriteStringTag(ref TTagState state, string key, string value); - protected abstract void WriteArrayTag(TTagState state, string key, TArrayState value); + protected abstract void WriteArrayTag(ref TTagState state, string key, ref TArrayState value); protected abstract void OnUnsupportedTagDropped( string tagKey, @@ -122,40 +122,43 @@ protected abstract void OnUnsupportedTagDropped( : value; } - private void WriteArrayTagInternal(TTagState state, string key, Array array, int? tagValueMaxLength) + private void WriteArrayTagInternal(ref TTagState state, string key, Array array, int? tagValueMaxLength) { var arrayState = this.arrayWriter.BeginWriteArray(); // This switch ensures the values of the resultant array-valued tag are of the same type. switch (array) { - case char[] charArray: this.WriteToArray(arrayState, charArray); break; - case string[]: this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); break; - case bool[] boolArray: this.WriteToArray(arrayState, boolArray); break; - case byte[] byteArray: this.WriteToArray(arrayState, byteArray); break; - case sbyte[] sbyteArray: this.WriteToArray(arrayState, sbyteArray); break; - case short[] shortArray: this.WriteToArray(arrayState, shortArray); break; - case ushort[] ushortArray: this.WriteToArray(arrayState, ushortArray); break; - case uint[] uintArray: this.WriteToArray(arrayState, uintArray); break; + case char[] charArray: this.WriteToArray(ref arrayState, charArray); break; + case string[]: this.ConvertToStringArrayThenWriteArrayTag(ref arrayState, array, tagValueMaxLength); break; + case bool[] boolArray: this.WriteToArray(ref arrayState, boolArray); break; + case byte[] byteArray: this.WriteToArray(ref arrayState, byteArray); break; + case sbyte[] sbyteArray: this.WriteToArray(ref arrayState, sbyteArray); break; + case short[] shortArray: this.WriteToArray(ref arrayState, shortArray); break; + case ushort[] ushortArray: this.WriteToArray(ref arrayState, ushortArray); break; #if NETFRAMEWORK - case int[]: this.WriteArrayTagIntNetFramework(arrayState, array, tagValueMaxLength); break; - case long[]: this.WriteArrayTagLongNetFramework(arrayState, array, tagValueMaxLength); break; + case int[]: this.WriteArrayTagIntNetFramework(ref arrayState, array, tagValueMaxLength); break; #else - case int[] intArray: this.WriteToArray(arrayState, intArray); break; - case long[] longArray: this.WriteToArray(arrayState, longArray); break; + case int[] intArray: this.WriteToArray(ref arrayState, intArray); break; #endif - case float[] floatArray: this.WriteToArray(arrayState, floatArray); break; - case double[] doubleArray: this.WriteToArray(arrayState, doubleArray); break; - default: this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); break; + case uint[] uintArray: this.WriteToArray(ref arrayState, uintArray); break; +#if NETFRAMEWORK + case long[]: this.WriteArrayTagLongNetFramework(ref arrayState, array, tagValueMaxLength); break; +#else + case long[] longArray: this.WriteToArray(ref arrayState, longArray); break; +#endif + case float[] floatArray: this.WriteToArray(ref arrayState, floatArray); break; + case double[] doubleArray: this.WriteToArray(ref arrayState, doubleArray); break; + default: this.ConvertToStringArrayThenWriteArrayTag(ref arrayState, array, tagValueMaxLength); break; } - this.arrayWriter.EndWriteArray(arrayState); + this.arrayWriter.EndWriteArray(ref arrayState); - this.WriteArrayTag(state, key, arrayState); + this.WriteArrayTag(ref state, key, ref arrayState); } #if NETFRAMEWORK - private void WriteArrayTagIntNetFramework(TArrayState arrayState, Array array, int? tagValueMaxLength) + private void WriteArrayTagIntNetFramework(ref TArrayState arrayState, Array array, int? tagValueMaxLength) { // Note: On .NET Framework x86 nint[] & nuint[] fall into int[] case @@ -163,14 +166,14 @@ private void WriteArrayTagIntNetFramework(TArrayState arrayState, Array array, i if (arrayType == typeof(nint[]) || arrayType == typeof(nuint[])) { - this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); + this.ConvertToStringArrayThenWriteArrayTag(ref arrayState, array, tagValueMaxLength); return; } - this.WriteToArray(arrayState, (int[])array); + this.WriteToArray(ref arrayState, (int[])array); } - private void WriteArrayTagLongNetFramework(TArrayState arrayState, Array array, int? tagValueMaxLength) + private void WriteArrayTagLongNetFramework(ref TArrayState arrayState, Array array, int? tagValueMaxLength) { // Note: On .NET Framework x64 nint[] & nuint[] fall into long[] case @@ -178,20 +181,20 @@ private void WriteArrayTagLongNetFramework(TArrayState arrayState, Array array, if (arrayType == typeof(nint[]) || arrayType == typeof(nuint[])) { - this.ConvertToStringArrayThenWriteArrayTag(arrayState, array, tagValueMaxLength); + this.ConvertToStringArrayThenWriteArrayTag(ref arrayState, array, tagValueMaxLength); return; } - this.WriteToArray(arrayState, (long[])array); + this.WriteToArray(ref arrayState, (long[])array); } #endif - private void ConvertToStringArrayThenWriteArrayTag(TArrayState arrayState, Array array, int? tagValueMaxLength) + private void ConvertToStringArrayThenWriteArrayTag(ref TArrayState arrayState, Array array, int? tagValueMaxLength) { if (array is string?[] arrayAsStringArray && (!tagValueMaxLength.HasValue || !arrayAsStringArray.Any(s => s?.Length > tagValueMaxLength))) { - this.WriteStringsToArray(arrayState, arrayAsStringArray); + this.WriteStringsToArray(ref arrayState, arrayAsStringArray); } else { @@ -206,7 +209,7 @@ private void ConvertToStringArrayThenWriteArrayTag(TArrayState arrayState, Array : TruncateString(Convert.ToString(item, CultureInfo.InvariantCulture), tagValueMaxLength); } - this.WriteStringsToArray(arrayState, new(stringArray, 0, array.Length)); + this.WriteStringsToArray(ref arrayState, new(stringArray, 0, array.Length)); } finally { @@ -215,54 +218,54 @@ private void ConvertToStringArrayThenWriteArrayTag(TArrayState arrayState, Array } } - private void WriteToArray(TArrayState arrayState, TItem[] array) + private void WriteToArray(ref TArrayState arrayState, TItem[] array) where TItem : struct { foreach (TItem item in array) { if (typeof(TItem) == typeof(char)) { - this.arrayWriter.WriteStringTag(arrayState, Convert.ToString((char)(object)item)!); + this.arrayWriter.WriteStringTag(ref arrayState, Convert.ToString((char)(object)item)!); } else if (typeof(TItem) == typeof(bool)) { - this.arrayWriter.WriteBooleanTag(arrayState, (bool)(object)item); + this.arrayWriter.WriteBooleanTag(ref arrayState, (bool)(object)item); } else if (typeof(TItem) == typeof(byte)) { - this.arrayWriter.WriteIntegralTag(arrayState, (byte)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (byte)(object)item); } else if (typeof(TItem) == typeof(sbyte)) { - this.arrayWriter.WriteIntegralTag(arrayState, (sbyte)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (sbyte)(object)item); } else if (typeof(TItem) == typeof(short)) { - this.arrayWriter.WriteIntegralTag(arrayState, (short)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (short)(object)item); } else if (typeof(TItem) == typeof(ushort)) { - this.arrayWriter.WriteIntegralTag(arrayState, (ushort)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (ushort)(object)item); } else if (typeof(TItem) == typeof(int)) { - this.arrayWriter.WriteIntegralTag(arrayState, (int)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (int)(object)item); } else if (typeof(TItem) == typeof(uint)) { - this.arrayWriter.WriteIntegralTag(arrayState, (uint)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (uint)(object)item); } else if (typeof(TItem) == typeof(long)) { - this.arrayWriter.WriteIntegralTag(arrayState, (long)(object)item); + this.arrayWriter.WriteIntegralTag(ref arrayState, (long)(object)item); } else if (typeof(TItem) == typeof(float)) { - this.arrayWriter.WriteFloatingPointTag(arrayState, (float)(object)item); + this.arrayWriter.WriteFloatingPointTag(ref arrayState, (float)(object)item); } else if (typeof(TItem) == typeof(double)) { - this.arrayWriter.WriteFloatingPointTag(arrayState, (double)(object)item); + this.arrayWriter.WriteFloatingPointTag(ref arrayState, (double)(object)item); } else { @@ -273,17 +276,17 @@ private void WriteToArray(TArrayState arrayState, TItem[] array) } } - private void WriteStringsToArray(TArrayState arrayState, ReadOnlySpan data) + private void WriteStringsToArray(ref TArrayState arrayState, ReadOnlySpan data) { foreach (var item in data) { if (item == null) { - this.arrayWriter.WriteNullTag(arrayState); + this.arrayWriter.WriteNullTag(ref arrayState); } else { - this.arrayWriter.WriteStringTag(arrayState, item); + this.arrayWriter.WriteStringTag(ref arrayState, item); } } } From 3554b317c0cdc4f0f091ce4d5e81a4686f380f71 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 8 Apr 2024 14:42:18 -0700 Subject: [PATCH 4/4] Fix missing changes from code review. --- src/Shared/TagWriter/TagWriter.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs index a8a749534eb..14bccf236cf 100644 --- a/src/Shared/TagWriter/TagWriter.cs +++ b/src/Shared/TagWriter/TagWriter.cs @@ -136,15 +136,12 @@ private void WriteArrayTagInternal(ref TTagState state, string key, Array array, case sbyte[] sbyteArray: this.WriteToArray(ref arrayState, sbyteArray); break; case short[] shortArray: this.WriteToArray(ref arrayState, shortArray); break; case ushort[] ushortArray: this.WriteToArray(ref arrayState, ushortArray); break; -#if NETFRAMEWORK - case int[]: this.WriteArrayTagIntNetFramework(ref arrayState, array, tagValueMaxLength); break; -#else - case int[] intArray: this.WriteToArray(ref arrayState, intArray); break; -#endif case uint[] uintArray: this.WriteToArray(ref arrayState, uintArray); break; #if NETFRAMEWORK + case int[]: this.WriteArrayTagIntNetFramework(ref arrayState, array, tagValueMaxLength); break; case long[]: this.WriteArrayTagLongNetFramework(ref arrayState, array, tagValueMaxLength); break; #else + case int[] intArray: this.WriteToArray(ref arrayState, intArray); break; case long[] longArray: this.WriteToArray(ref arrayState, longArray); break; #endif case float[] floatArray: this.WriteToArray(ref arrayState, floatArray); break;