Skip to content

Commit

Permalink
Refactor WriteAsciiStringToBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Jan 29, 2021
1 parent fd117d0 commit 59fa86f
Showing 1 changed file with 30 additions and 28 deletions.
58 changes: 30 additions & 28 deletions csharp/src/Google.Protobuf/WritingPrimitives.cs
Expand Up @@ -32,9 +32,10 @@

using System;
using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NET5_0
#if GOOGLE_PROTOBUF_SIMD
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
Expand Down Expand Up @@ -194,22 +195,7 @@ public static void WriteString(ref Span<byte> buffer, ref WriterInternalState st
{
if (length == value.Length) // Must be all ASCII...
{
ref char sourceChars = ref MemoryMarshal.GetReference(value.AsSpan());
ref byte destinationBytes = ref MemoryMarshal.GetReference(buffer.Slice(state.position));

// If 64bit, process 4 chars at a time.
int currentIndex = IntPtr.Size == 8
? WriteAsciiStringToBuffer(ref sourceChars, ref destinationBytes, value, length)
: 0;

// Process any remaining, 1 char at a time.
// Avoid bounds checking with ref + Unsafe
for (; currentIndex < length; currentIndex++)
{
Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex) = (byte)Unsafe.AddByteOffset(ref sourceChars, (IntPtr)(currentIndex * 2));
}

state.position += length;
WriteAsciiStringToBuffer(buffer, ref state, value, length);
}
else
{
Expand All @@ -227,23 +213,39 @@ public static void WriteString(ref Span<byte> buffer, ref WriterInternalState st
}
}

private static int WriteAsciiStringToBuffer(ref char sourceChars, ref byte destinationBytes, string value, int length)
private static void WriteAsciiStringToBuffer(Span<byte> buffer, ref WriterInternalState state, string value, int length)
{
ref byte sourceBytes = ref Unsafe.As<char, byte>(ref sourceChars);
ref char sourceChars = ref MemoryMarshal.GetReference(value.AsSpan());
ref byte destinationBytes = ref MemoryMarshal.GetReference(buffer.Slice(state.position));

// Process 4 chars at a time until there are less than 4 remaining.
// We already know all characters are ASCII so there is no need to validate the source.
int lastIndexWhereCanReadFourChars = value.Length - 4;
int currentIndex = 0;
do
// If 64bit, process 4 chars at a time.
if (IntPtr.Size == 8)
{
NarrowFourUtf16CharsToAsciiAndWriteToBuffer(
ref Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex),
Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref sourceBytes, (IntPtr)(currentIndex * 2))));
Debug.Assert(length >= 4);

ref byte sourceBytes = ref Unsafe.As<char, byte>(ref sourceChars);

} while ((currentIndex += 4) <= lastIndexWhereCanReadFourChars);
// Process 4 chars at a time until there are less than 4 remaining.
// We already know all characters are ASCII so there is no need to validate the source.
int lastIndexWhereCanReadFourChars = value.Length - 4;
do
{
NarrowFourUtf16CharsToAsciiAndWriteToBuffer(
ref Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex),
Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref sourceBytes, (IntPtr)(currentIndex * 2))));

} while ((currentIndex += 4) <= lastIndexWhereCanReadFourChars);
}

// Process any remaining, 1 char at a time.
// Avoid bounds checking with ref + Unsafe
for (; currentIndex < length; currentIndex++)
{
Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex) = (byte)Unsafe.AddByteOffset(ref sourceChars, (IntPtr)(currentIndex * 2));
}

return currentIndex;
state.position += length;
}

// Copied with permission from https://github.com/dotnet/runtime/blob/1cdafd27e4afd2c916af5df949c13f8b373c4335/src/libraries/System.Private.CoreLib/src/System/Text/ASCIIUtility.cs#L1119-L1171
Expand Down

0 comments on commit 59fa86f

Please sign in to comment.