mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 15:11:53 +08:00
BinaryDecoder: zero-length arrays share Array.Empty
ReadArrayElements and ReadFixedWidthArray allocated a new T[0] for a zero-length array, which every response header has (StringTable, DiagnosticInfos). A length of -1 still decodes as a null array. ServerLoadHarness read --nodes 1 (20 sessions, 4 alternating rounds): client allocB/op 1871 -> 1822 on top of the channel timer change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
77e2ca0b5a
commit
730cec1985
@@ -2432,6 +2432,12 @@ namespace Opc.Ua
|
|||||||
private T[] ReadArrayElements<T, TReader>(int length, TReader reader)
|
private T[] ReadArrayElements<T, TReader>(int length, TReader reader)
|
||||||
where TReader : struct, IElementReader<T>
|
where TReader : struct, IElementReader<T>
|
||||||
{
|
{
|
||||||
|
// an empty array is immutable: all share one instance.
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
return Array.Empty<T>();
|
||||||
|
}
|
||||||
|
|
||||||
int capacity = GetInitialArrayCapacity<T>(length);
|
int capacity = GetInitialArrayCapacity<T>(length);
|
||||||
if (capacity < length)
|
if (capacity < length)
|
||||||
{
|
{
|
||||||
@@ -2526,6 +2532,11 @@ namespace Opc.Ua
|
|||||||
/// against the remaining bytes by the caller.</param>
|
/// against the remaining bytes by the caller.</param>
|
||||||
private T[] ReadFixedWidthArray<T>(int length) where T : unmanaged
|
private T[] ReadFixedWidthArray<T>(int length) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
return Array.Empty<T>();
|
||||||
|
}
|
||||||
|
|
||||||
// The caller verified the length against the remaining bytes. If
|
// The caller verified the length against the remaining bytes. If
|
||||||
// they are unknown the array grows with the bytes actually read.
|
// they are unknown the array grows with the bytes actually read.
|
||||||
int capacity = GetRemainingLength() >= 0
|
int capacity = GetRemainingLength() >= 0
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Opc.Ua.Tests;
|
using Opc.Ua.Tests;
|
||||||
@@ -767,6 +768,33 @@ namespace Opc.Ua.Types.Tests.Encoders
|
|||||||
Assert.That(result.Count, Is.Zero);
|
Assert.That(result.Count, Is.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zero-length arrays, such as the empty StringTable of every response
|
||||||
|
/// header, decode as empty, not null, and allocate nothing.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void ReadZeroLengthArraysReturnsEmptyWithoutAllocating()
|
||||||
|
{
|
||||||
|
ITelemetryContext telemetryContext = NUnitTelemetryContext.Create();
|
||||||
|
var messageContext = ServiceMessageContext.CreateEmpty(telemetryContext);
|
||||||
|
byte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF];
|
||||||
|
using var decoder = new BinaryDecoder(buffer, messageContext);
|
||||||
|
|
||||||
|
ArrayOf<string?> strings = decoder.ReadStringArray(null);
|
||||||
|
ArrayOf<int> integers = decoder.ReadInt32Array(null);
|
||||||
|
ArrayOf<string?> nullStrings = decoder.ReadStringArray(null);
|
||||||
|
|
||||||
|
Assert.That(strings.IsNull, Is.False);
|
||||||
|
Assert.That(strings.Count, Is.Zero);
|
||||||
|
Assert.That(MemoryMarshal.TryGetArray(strings.Memory, out ArraySegment<string?> stringArray), Is.True);
|
||||||
|
Assert.That(stringArray.Array, Is.SameAs(Array.Empty<string?>()));
|
||||||
|
Assert.That(integers.IsNull, Is.False);
|
||||||
|
Assert.That(integers.Count, Is.Zero);
|
||||||
|
Assert.That(MemoryMarshal.TryGetArray(integers.Memory, out ArraySegment<int> integerArray), Is.True);
|
||||||
|
Assert.That(integerArray.Array, Is.SameAs(Array.Empty<int>()));
|
||||||
|
Assert.That(nullStrings.IsNull, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ReadEncodeableArrayReturnsMultipleElements()
|
public void ReadEncodeableArrayReturnsMultipleElements()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user