8
8
#endregion
9
9
10
10
using System;
11
+
using System.Buffers;
11
12
using System.Collections;
12
13
using System.Collections.Generic;
13
14
using System.Diagnostics;
14
15
using System.IO;
15
16
using System.Linq;
17
+
using System.Runtime.InteropServices;
16
18
using System.Security;
17
19
#if NET5_0_OR_GREATER
18
20
using System.Runtime.CompilerServices;
@@ -116,12 +118,24 @@ public void AddEntriesFrom(ref ParseContext ctx, FieldCodec<T> codec)
116
118
{
117
119
EnsureSize(count + (length / codec.FixedSize));
118
120
119
-
while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
121
+
// if littleEndian treat array as bytes and directly copy from buffer for improved performance
122
+
if(TryGetArrayAsSpanPinnedUnsafe(codec, out Span<byte> span, out GCHandle handle))
123
+
{
124
+
span = span.Slice(count * codec.FixedSize);
125
+
Debug.Assert(span.Length >= length);
126
+
ParsingPrimitives.ReadPackedFieldLittleEndian(ref ctx.buffer, ref ctx.state, length, span);
127
+
count += length / codec.FixedSize;
128
+
handle.Free();
129
+
}
130
+
else
120
131
{
121
-
// Only FieldCodecs with a fixed size can reach here, and they are all known
122
-
// types that don't allow the user to specify a custom reader action.
123
-
// reader action will never return null.
124
-
array[count++] = reader(ref ctx);
132
+
while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
133
+
{
134
+
// Only FieldCodecs with a fixed size can reach here, and they are all known
135
+
// types that don't allow the user to specify a custom reader action.
136
+
// reader action will never return null.
137
+
array[count++] = reader(ref ctx);
138
+
}
125
139
}
126
140
}
127
141
else
@@ -241,9 +255,21 @@ public void WriteTo(ref WriteContext ctx, FieldCodec<T> codec)
241
255
int size = CalculatePackedDataSize(codec);
242
256
ctx.WriteTag(tag);
243
257
ctx.WriteLength(size);
244
-
for (int i = 0; i < count; i++)
258
+
259
+
// if littleEndian and elements has fixed size, treat array as bytes (and write it as bytes to buffer) for improved performance
260
+
if(TryGetArrayAsSpanPinnedUnsafe(codec, out Span<byte> span, out GCHandle handle))
245
261
{
246
-
writer(ref ctx, array[i]);
262
+
span = span.Slice(0, Count * codec.FixedSize);
263
+
264
+
WritingPrimitives.WriteRawBytes(ref ctx.buffer, ref ctx.state, span);
265
+
handle.Free();
266
+
}
267
+
else
268
+
{
269
+
for (int i = 0; i < count; i++)
270
+
{
271
+
writer(ref ctx, array[i]);
272
+
}
247
273
}
248
274
}
249
275
else
@@ -679,6 +705,24 @@ internal void SetCount(int targetCount)
679
705
count = targetCount;
680
706
}
681
707
708
+
[SecuritySafeCritical]
709
+
private unsafe bool TryGetArrayAsSpanPinnedUnsafe(FieldCodec<T> codec, out Span<byte> span, out GCHandle handle)
710
+
{
711
+
// 1. protobuf wire bytes is LittleEndian only
712
+
// 2. validate that size of csharp element T is matching the size of protobuf wire size
713
+
// NOTE: cannot use bool with this span because csharp marshal it as 4 bytes
714
+
if (BitConverter.IsLittleEndian && (codec.FixedSize > 0 && Marshal.SizeOf(typeof(T)) == codec.FixedSize))
715
+
{
716
+
handle = GCHandle.Alloc(array, GCHandleType.Pinned);
717
+
span = new Span<byte>(handle.AddrOfPinnedObject().ToPointer(), array.Length * codec.FixedSize);
718
+
return true;
719
+
}
720
+
721
+
span = default;
722
+
handle = default;
723
+
return false;
724
+
}
725
+
682
726
#region Explicit interface implementation for IList and ICollection.
683
727
bool IList.IsFixedSize => false;
684
728
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4