Skip to content

Commit 64aa83e

Browse files
Big-endian fixes: binary data in RenderBatch (#36221)
* Use little-endian accessors for RenderBatch binary data throughout * Fixes part 3 of #35709 Co-authored-by: Ulrich Weigand <[email protected]>
1 parent a32d3be commit 64aa83e

File tree

3 files changed

+30
-27
lines changed

3 files changed

+30
-27
lines changed

src/Components/Ignitor/src/RenderBatchReader.cs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Buffers.Binary;
56
using System.Text;
67

78
#nullable enable
@@ -29,7 +30,7 @@ private static string[] ReadStringTable(ReadOnlySpan<byte> data, ReadOnlySpan<by
2930

3031
for (var i = 0; i < indexes.Length; i += 4)
3132
{
32-
var index = BitConverter.ToInt32(indexes.Slice(i, 4));
33+
var index = BinaryPrimitives.ReadInt32LittleEndian(indexes.Slice(i, 4));
3334

3435
// The string table entries are all length-prefixed UTF8 blobs
3536
var length = (int)ReadUnsignedLEB128(data, index, out var numLEB128Bytes);
@@ -46,21 +47,21 @@ private static ArrayRange<RenderTreeDiff> ReadUpdatedComponents(ReadOnlySpan<byt
4647

4748
for (var i = 0; i < indexes.Length; i += 4)
4849
{
49-
var index = BitConverter.ToInt32(indexes.Slice(i, 4));
50+
var index = BinaryPrimitives.ReadInt32LittleEndian(indexes.Slice(i, 4));
5051

51-
var componentId = BitConverter.ToInt32(data.Slice(index, 4));
52-
var editCount = BitConverter.ToInt32(data.Slice(index + 4, 4));
52+
var componentId = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(index, 4));
53+
var editCount = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(index + 4, 4));
5354

5455
var editData = data.Slice(index + 8);
5556
var edits = new RenderTreeEdit[editCount];
5657
for (var j = 0; j < editCount; j++)
5758
{
58-
var type = (RenderTreeEditType)BitConverter.ToInt32(editData.Slice(0, 4));
59-
var siblingIndex = BitConverter.ToInt32(editData.Slice(4, 4));
59+
var type = (RenderTreeEditType)BinaryPrimitives.ReadInt32LittleEndian(editData.Slice(0, 4));
60+
var siblingIndex = BinaryPrimitives.ReadInt32LittleEndian(editData.Slice(4, 4));
6061

6162
// ReferenceFrameIndex and MoveToSiblingIndex share a slot, so this reads
6263
// whichever one applies to the edit type
63-
var referenceFrameIndex = BitConverter.ToInt32(editData.Slice(8, 4));
64+
var referenceFrameIndex = BinaryPrimitives.ReadInt32LittleEndian(editData.Slice(8, 4));
6465
var removedAttributeName = ReadString(editData.Slice(12, 4), strings);
6566

6667
editData = editData.Slice(16);
@@ -133,7 +134,7 @@ private static ArrayRange<RenderTreeFrame> ReadReferenceFrames(ReadOnlySpan<byte
133134
{
134135
var frameData = data.Slice(i, ReferenceFrameSize);
135136

136-
var type = (RenderTreeFrameType)BitConverter.ToInt32(frameData.Slice(0, 4));
137+
var type = (RenderTreeFrameType)BinaryPrimitives.ReadInt32LittleEndian(frameData.Slice(0, 4));
137138

138139
// We want each frame to take up the same number of bytes, so that the
139140
// recipient can index into the array directly instead of having to
@@ -146,13 +147,13 @@ private static ArrayRange<RenderTreeFrame> ReadReferenceFrames(ReadOnlySpan<byte
146147
case RenderTreeFrameType.Attribute:
147148
var attributeName = ReadString(frameData.Slice(4, 4), strings);
148149
var attributeValue = ReadString(frameData.Slice(8, 4), strings);
149-
var attributeEventHandlerId = BitConverter.ToUInt64(frameData.Slice(12, 8));
150+
var attributeEventHandlerId = BinaryPrimitives.ReadUInt64LittleEndian(frameData.Slice(12, 8));
150151
result[i / ReferenceFrameSize] = RenderTreeFrame.Attribute(0, attributeName, attributeValue).WithAttributeEventHandlerId(attributeEventHandlerId);
151152
break;
152153

153154
case RenderTreeFrameType.Component:
154-
var componentSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
155-
var componentId = BitConverter.ToInt32(frameData.Slice(8, 4)); // Nowhere to put this without creating a ComponentState
155+
var componentSubtreeLength = BinaryPrimitives.ReadInt32LittleEndian(frameData.Slice(4, 4));
156+
var componentId = BinaryPrimitives.ReadInt32LittleEndian(frameData.Slice(8, 4)); // Nowhere to put this without creating a ComponentState
156157
result[i / ReferenceFrameSize] = RenderTreeFrame.ChildComponent(0, componentType: null)
157158
.WithComponentSubtreeLength(componentSubtreeLength)
158159
.WithComponent(new ComponentState(componentId));
@@ -164,7 +165,7 @@ private static ArrayRange<RenderTreeFrame> ReadReferenceFrames(ReadOnlySpan<byte
164165
break;
165166

166167
case RenderTreeFrameType.Element:
167-
var elementSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
168+
var elementSubtreeLength = BinaryPrimitives.ReadInt32LittleEndian(frameData.Slice(4, 4));
168169
var elementName = ReadString(frameData.Slice(8, 4), strings);
169170
result[i / ReferenceFrameSize] = RenderTreeFrame.Element(0, elementName).WithElementSubtreeLength(elementSubtreeLength);
170171
break;
@@ -176,7 +177,7 @@ private static ArrayRange<RenderTreeFrame> ReadReferenceFrames(ReadOnlySpan<byte
176177
break;
177178

178179
case RenderTreeFrameType.Region:
179-
var regionSubtreeLength = BitConverter.ToInt32(frameData.Slice(4, 4));
180+
var regionSubtreeLength = BinaryPrimitives.ReadInt32LittleEndian(frameData.Slice(4, 4));
180181
result[i / ReferenceFrameSize] = RenderTreeFrame.Region(0).WithRegionSubtreeLength(regionSubtreeLength);
181182
break;
182183

@@ -210,7 +211,7 @@ private static ArrayRange<ulong> ReadDisposedEventHandlerIds(ReadOnlySpan<byte>
210211

211212
private static string? ReadString(ReadOnlySpan<byte> data, string[] strings)
212213
{
213-
var index = BitConverter.ToInt32(data.Slice(0, 4));
214+
var index = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
214215
return index >= 0 ? strings[index] : null;
215216
}
216217

@@ -237,11 +238,11 @@ private readonly struct Sections
237238
public static Sections Parse(ReadOnlySpan<byte> data)
238239
{
239240
return new Sections(
240-
BitConverter.ToInt32(data.Slice(data.Length - 20, 4)),
241-
BitConverter.ToInt32(data.Slice(data.Length - 16, 4)),
242-
BitConverter.ToInt32(data.Slice(data.Length - 12, 4)),
243-
BitConverter.ToInt32(data.Slice(data.Length - 8, 4)),
244-
BitConverter.ToInt32(data.Slice(data.Length - 4, 4)));
241+
BinaryPrimitives.ReadInt32LittleEndian(data.Slice(data.Length - 20, 4)),
242+
BinaryPrimitives.ReadInt32LittleEndian(data.Slice(data.Length - 16, 4)),
243+
BinaryPrimitives.ReadInt32LittleEndian(data.Slice(data.Length - 12, 4)),
244+
BinaryPrimitives.ReadInt32LittleEndian(data.Slice(data.Length - 8, 4)),
245+
BinaryPrimitives.ReadInt32LittleEndian(data.Slice(data.Length - 4, 4)));
245246
}
246247

247248
private readonly int _updatedComponents;
@@ -262,14 +263,14 @@ public Sections(int updatedComponents, int referenceFrames, int disposedComponen
262263
public ReadOnlySpan<byte> GetUpdatedComponentIndexes(ReadOnlySpan<byte> data)
263264
{
264265
// This is count-prefixed contiguous array of of integers.
265-
var count = BitConverter.ToInt32(data.Slice(_updatedComponents, 4));
266+
var count = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(_updatedComponents, 4));
266267
return data.Slice(_updatedComponents + 4, count * 4);
267268
}
268269

269270
public ReadOnlySpan<byte> GetReferenceFrameData(ReadOnlySpan<byte> data)
270271
{
271272
// This is a count-prefixed contiguous array of RenderTreeFrame.
272-
var count = BitConverter.ToInt32(data.Slice(_referenceFrames, 4));
273+
var count = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(_referenceFrames, 4));
273274
return data.Slice(_referenceFrames + 4, count * ReferenceFrameSize);
274275
}
275276

src/Components/Ignitor/test/RenderBatchReaderTest.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Buffers.Binary;
56
using System.Collections.Generic;
67
using System.Globalization;
78
using System.IO;
@@ -232,7 +233,7 @@ static string[] ReadStringTable(Span<byte> data)
232233

233234
// The string table position is given by the final int, and continues
234235
// until we get to the final set of top-level indices
235-
var stringTableStartPosition = BitConverter.ToInt32(bytes, bytes.Length - 4);
236+
var stringTableStartPosition = ReadInt(bytes, bytes.Length - 4);
236237
var stringTableEndPositionExcl = bytes.Length - 20;
237238

238239
var result = new List<string>();
@@ -241,7 +242,7 @@ static string[] ReadStringTable(Span<byte> data)
241242
entryPosition += 4)
242243
{
243244
// The string table entries are all length-prefixed UTF8 blobs
244-
var tableEntryPos = BitConverter.ToInt32(bytes, entryPosition);
245+
var tableEntryPos = ReadInt(bytes, entryPosition);
245246
var length = (int)ReadUnsignedLEB128(bytes, tableEntryPos, out var numLEB128Bytes);
246247
var value = Encoding.UTF8.GetString(bytes, tableEntryPos + numLEB128Bytes, length);
247248
result.Add(value);
@@ -299,7 +300,7 @@ static void AssertBinaryContents(Span<byte> data, int startIndex, params object[
299300
}
300301

301302
static int ReadInt(Span<byte> bytes, int startOffset)
302-
=> BitConverter.ToInt32(bytes.Slice(startOffset, 4).ToArray(), 0);
303+
=> BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(startOffset, 4));
303304

304305
public static uint ReadUnsignedLEB128(byte[] bytes, int startOffset, out int numBytesRead)
305306
{

src/Components/Server/test/Circuits/RenderBatchWriterTest.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Buffers.Binary;
56
using System.Collections.Generic;
67
using System.Globalization;
78
using System.IO;
@@ -283,7 +284,7 @@ static string[] ReadStringTable(Span<byte> data)
283284

284285
// The string table position is given by the final int, and continues
285286
// until we get to the final set of top-level indices
286-
var stringTableStartPosition = BitConverter.ToInt32(bytes, bytes.Length - 4);
287+
var stringTableStartPosition = ReadInt(bytes, bytes.Length - 4);
287288
var stringTableEndPositionExcl = bytes.Length - 20;
288289

289290
var result = new List<string>();
@@ -292,7 +293,7 @@ static string[] ReadStringTable(Span<byte> data)
292293
entryPosition += 4)
293294
{
294295
// The string table entries are all length-prefixed UTF8 blobs
295-
var tableEntryPos = BitConverter.ToInt32(bytes, entryPosition);
296+
var tableEntryPos = ReadInt(bytes, entryPosition);
296297
var length = (int)ReadUnsignedLEB128(bytes, tableEntryPos, out var numLEB128Bytes);
297298
var value = Encoding.UTF8.GetString(bytes, tableEntryPos + numLEB128Bytes, length);
298299
result.Add(value);
@@ -350,7 +351,7 @@ static void AssertBinaryContents(Span<byte> data, int startIndex, params object[
350351
}
351352

352353
static int ReadInt(Span<byte> bytes, int startOffset)
353-
=> BitConverter.ToInt32(bytes.Slice(startOffset, 4).ToArray(), 0);
354+
=> BinaryPrimitives.ReadInt32LittleEndian(bytes.Slice(startOffset, 4));
354355

355356
public static uint ReadUnsignedLEB128(byte[] bytes, int startOffset, out int numBytesRead)
356357
{

0 commit comments

Comments
 (0)