Skip to content

Commit 95053e6

Browse files
authored
Correct span and non-span versions of ToArray() and ToArrayPadded() methods (#8734)
* Correction of bug inside ToArray<T> methods Avoid allocating too large buffers (len is expressed in bytes, not in Ts). Added validation to ensure len is a multiple of SizeOf<T>() before converting to array. * Update ByteBuffer.cs * Refactor ToArray and ToArrayPadded methods I understand from failed test that pos, len, padLeft and padRight are expressed in Ts * Refactor ToArray and ToArrayPadded methods * Final correction All functions parameters expressed in bytes for homogeneity Tests run: - UNSAFE_BYTEBUFFER=true/ENABLE_SPAN_T=true: passed - UNSAFE_BYTEBUFFER=true/ENABLE_SPAN_T=false: passed - UNSAFE_BYTEBUFFER=false/ENABLE_SPAN_T=false: passed - UNSAFE_BYTEBUFFER=false/ENABLE_SPAN_T=true: configuration forbidden by compilation Correction of FlatBuffers.Test.csproj to allow UNSAFE_BYTEBUFFER/ENABLE_SPAN_T tests Correction of FlatBuffersExampleTests.cs: I think the test was not run because it could not pass (to be reviewed carefully)
1 parent 27325e0 commit 95053e6

File tree

4 files changed

+68
-45
lines changed

4 files changed

+68
-45
lines changed

net/FlatBuffers/ByteBuffer.cs

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@
3838

3939
using System;
4040
using System.Collections.Generic;
41-
using System.IO;
42-
using System.Runtime.CompilerServices;
4341
using System.Runtime.InteropServices;
4442
using System.Text;
4543

4644
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
4745
using System.Buffers.Binary;
46+
#else
47+
using System.IO;
4848
#endif
4949

5050
#if ENABLE_SPAN_T && !UNSAFE_BYTEBUFFER
@@ -245,34 +245,36 @@ public static int ArraySize<T>(Span<T> x)
245245
#endif
246246

247247
// Get a portion of the buffer casted into an array of type T, given
248-
// the buffer position and length.
249-
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
250-
public T[] ToArray<T>(int pos, int len)
248+
// the buffer position (in bytes) and length (in bytes).
249+
public T[] ToArray<T>(int posInBytes, int lenInBytes)
251250
where T : struct
252251
{
253-
AssertOffsetAndLength(pos, len);
254-
return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(pos)).Slice(0, len).ToArray();
255-
}
252+
AssertOffsetAndLength(posInBytes, lenInBytes);
253+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
254+
return MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(posInBytes, lenInBytes)).ToArray();
256255
#else
257-
public T[] ToArray<T>(int pos, int len)
258-
where T : struct
259-
{
260-
AssertOffsetAndLength(pos, len);
261-
T[] arr = new T[len];
262-
Buffer.BlockCopy(_buffer.Buffer, pos, arr, 0, ArraySize(arr));
263-
return arr;
264-
}
256+
var lenInTs = ConvertBytesToTs<T>(lenInBytes);
257+
var arrayOfTs = new T[lenInTs];
258+
Buffer.BlockCopy(_buffer.Buffer, posInBytes, arrayOfTs, 0, lenInBytes);
259+
return arrayOfTs;
265260
#endif
261+
}
266262

267-
public T[] ToArrayPadded<T>(int pos, int len, int padLeft, int padRight)
263+
public T[] ToArrayPadded<T>(int posInBytes, int lenInBytes, int padLeftInBytes, int padRightInBytes)
268264
where T : struct
269265
{
270-
AssertOffsetAndLength(pos, len);
271-
int totalBytes = padLeft + len + padRight;
272-
byte[] raw = _buffer.Buffer;
273-
T[] arr = new T[totalBytes];
274-
Buffer.BlockCopy(raw, pos, arr, padLeft, len);
275-
return arr;
266+
AssertOffsetAndLength(posInBytes, lenInBytes);
267+
var padLeftInTs = ConvertBytesToTs<T>(padLeftInBytes);
268+
var lenInTs = ConvertBytesToTs<T>(lenInBytes);
269+
var padRightInTs = ConvertBytesToTs<T>(padRightInBytes);
270+
var sizeInTs = padLeftInTs + lenInTs + padRightInTs;
271+
var arrayOfTs = new T[sizeInTs];
272+
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
273+
MemoryMarshal.Cast<byte, T>(_buffer.ReadOnlySpan.Slice(posInBytes, lenInBytes)).CopyTo(arrayOfTs.AsSpan().Slice(padLeftInTs));
274+
#else
275+
Buffer.BlockCopy(_buffer.Buffer, posInBytes, arrayOfTs, padLeftInBytes, lenInBytes);
276+
#endif
277+
return arrayOfTs;
276278
}
277279

278280
public byte[] ToSizedArrayPadded(int padLeft, int padRight)
@@ -455,9 +457,31 @@ private void AssertOffsetAndLength(int offset, int length)
455457
#endif
456458
}
457459

460+
public static int ConvertTsToBytes<T>(int valueInTs)
461+
where T : struct
462+
{
463+
var sizeOfT = SizeOf<T>();
464+
var valueInBytes = valueInTs * sizeOfT;
465+
return valueInBytes;
466+
}
467+
468+
public static int ConvertBytesToTs<T>(int valueInBytes)
469+
where T : struct
470+
{
471+
var sizeOfT = SizeOf<T>();
472+
var valueInTs = valueInBytes / sizeOfT;
473+
#if !BYTEBUFFER_NO_BOUNDS_CHECK
474+
if (valueInTs * sizeOfT != valueInBytes)
475+
{
476+
throw new ArgumentException($"{valueInBytes} must be a multiple of SizeOf<{typeof(T).Name}>()={sizeOfT}");
477+
}
478+
#endif
479+
return valueInTs;
480+
}
481+
458482
#if ENABLE_SPAN_T && UNSAFE_BYTEBUFFER
459483

460-
public void PutSbyte(int offset, sbyte value)
484+
public void PutSbyte(int offset, sbyte value)
461485
{
462486
AssertOffsetAndLength(offset, sizeof(sbyte));
463487
_buffer.Span[offset] = (byte)value;

tests/FlatBuffers.Test/ByteBufferTests.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
using System;
18+
using System.Linq;
1819
using System.Runtime.InteropServices;
1920

2021
namespace Google.FlatBuffers.Test
@@ -363,34 +364,32 @@ public void ByteBuffer_To_Array_Float()
363364
fData[7] = 15.9994F;
364365
fData[8] = 18.9984F;
365366

367+
var sizeInBytes = ByteBuffer.ConvertTsToBytes<float>(fData.Length);
368+
366369
// Tranfer it to a byte array
367-
var buffer = new byte[sizeof(float) * fData.Length];
370+
var buffer = new byte[sizeInBytes];
368371
Buffer.BlockCopy(fData, 0, buffer, 0, buffer.Length);
369372

370373
// Create the Byte Buffer from byte array
371374
var uut = new ByteBuffer(buffer);
372375

373376
// Get the full array back out and ensure they are equivalent
374-
var bbArray = uut.ToArray<float>(0, len);
377+
var bbArray = uut.ToArray<float>(0, sizeInBytes);
375378
Assert.ArrayEqual(fData, bbArray);
376379

377380
// Get a portion of the full array back out and ensure the
378381
// subrange agrees
379-
var bbArray2 = uut.ToArray<float>(4, len - 1);
380-
Assert.AreEqual(bbArray2.Length, len - 1);
381-
for (int i = 1; i < len - 1; i++)
382-
{
383-
Assert.AreEqual(fData[i], bbArray2[i - 1]);
384-
}
382+
var posInFloats = 4;
383+
var lenInFloats = Math.Min(fData.Length - posInFloats - 1, 4);
384+
var bbArray2 = uut.ToArray<float>(ByteBuffer.ConvertTsToBytes<float>(posInFloats), ByteBuffer.ConvertTsToBytes<float>(lenInFloats));
385+
Assert.ArrayEqual(fData.Skip(posInFloats).Take(bbArray2.Length).ToArray(), bbArray2);
385386

386387
// Get a sub portion of the full array back out and ensure the
387388
// subrange agrees
388-
var bbArray3 = uut.ToArray<float>(8, len - 4);
389-
Assert.AreEqual(bbArray3.Length, len - 4);
390-
for (int i = 2; i < len - 4; i++)
391-
{
392-
Assert.AreEqual(fData[i], bbArray3[i - 2]);
393-
}
389+
posInFloats = 6;
390+
lenInFloats = Math.Min(fData.Length - posInFloats - 1, 1);
391+
var bbArray3 = uut.ToArray<float>(ByteBuffer.ConvertTsToBytes<float>(posInFloats), ByteBuffer.ConvertTsToBytes<float>(lenInFloats));
392+
Assert.ArrayEqual(fData.Skip(posInFloats).Take(bbArray3.Length).ToArray(), bbArray3);
394393
}
395394

396395
public void ByteBuffer_Put_Array_Helper<T>(T[] data, int typeSize)
@@ -405,7 +404,7 @@ public void ByteBuffer_Put_Array_Helper<T>(T[] data, int typeSize)
405404
Assert.AreEqual(1024 - typeSize * data.Length, nOffset);
406405

407406
// Get the full array back out and ensure they are equivalent
408-
var bbArray = uut.ToArray<T>(nOffset, data.Length);
407+
var bbArray = uut.ToArray<T>(nOffset, ByteBuffer.ConvertTsToBytes<T>(data.Length));
409408
Assert.ArrayEqual(data, bbArray);
410409
}
411410

@@ -421,7 +420,7 @@ public void ByteBuffer_Put_ArraySegment_Helper<T>(ArraySegment<T> data, int type
421420
Assert.AreEqual(1024 - typeSize * data.Count, nOffset);
422421

423422
// Get the full array back out and ensure they are equivalent
424-
var bbArray = uut.ToArray<T>(nOffset, data.Count);
423+
var bbArray = uut.ToArray<T>(nOffset, ByteBuffer.ConvertTsToBytes<T>(data.Count));
425424
Assert.ArrayEqual(data, bbArray);
426425
}
427426

@@ -443,7 +442,7 @@ public unsafe void ByteBuffer_Put_IntPtr_Helper<T>(T[] data, int typeSize)
443442
Assert.AreEqual(1024 - sizeInBytes, nOffset);
444443

445444
// Get the full array back out and ensure they are equivalent
446-
var bbArray = uut.ToArray<T>(nOffset, data.Length);
445+
var bbArray = uut.ToArray<T>(nOffset, ByteBuffer.ConvertTsToBytes<T>(data.Length));
447446
Assert.ArrayEqual(data, bbArray);
448447
}
449448
finally

tests/FlatBuffers.Test/FlatBuffers.Test.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
<Compile Remove="Properties\AssemblyInfo.cs" />
1010
</ItemGroup>
1111

12-
<PropertyGroup Condition="'$(UnsafeByteBuffer)' == 'true'">
12+
<PropertyGroup Condition="'$(UNSAFE_BYTEBUFFER)' == 'true'">
1313
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1414
<DefineConstants>$(DefineConstants);UNSAFE_BYTEBUFFER</DefineConstants>
1515
</PropertyGroup>
1616

17-
<PropertyGroup Condition="'$(EnableSpanT)' == 'true'">
17+
<PropertyGroup Condition="'$(ENABLE_SPAN_T)' == 'true'">
1818
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1919
<DefineConstants>$(DefineConstants);ENABLE_SPAN_T</DefineConstants>
2020
</PropertyGroup>

tests/FlatBuffers.Test/FlatBuffersExampleTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,10 @@ private void TestBuffer(ByteBuffer bb)
268268
}
269269

270270
var longArrayBytes = monster.GetVectorOfLongsBytes();
271-
Assert.IsTrue(monster.VectorOfLongsLength * 8 == longArrayBytes.Length);
271+
Assert.IsTrue(monster.VectorOfLongsLength == longArrayBytes.Length);
272272

273273
var doubleArrayBytes = monster.GetVectorOfDoublesBytes();
274-
Assert.IsTrue(monster.VectorOfDoublesLength * 8 == doubleArrayBytes.Length);
274+
Assert.IsTrue(monster.VectorOfDoublesLength == doubleArrayBytes.Length);
275275
#else
276276
var nameBytes = monster.GetNameBytes().Value;
277277
Assert.AreEqual("MyMonster", Encoding.UTF8.GetString(nameBytes.Array, nameBytes.Offset, nameBytes.Count));

0 commit comments

Comments
 (0)