Skip to content

Commit

Permalink
Simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Jan 28, 2021
1 parent b229721 commit d4df924
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 32 deletions.
27 changes: 26 additions & 1 deletion csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs
Expand Up @@ -519,7 +519,7 @@ public void Dispose_FromByteArray()
}

[Test]
public void WriteStringsOfDifferentSizes()
public void WriteStringsOfDifferentSizes_Ascii()
{
for (int i = 1; i <= 1024; i++)
{
Expand All @@ -540,5 +540,30 @@ public void WriteStringsOfDifferentSizes()
Assert.AreEqual(s, input.ReadString());
}
}

[Test]
public void WriteStringsOfDifferentSizes_Unicode()
{
for (int i = 1; i <= 1024; i++)
{
var buffer = new byte[4096];
var output = new CodedOutputStream(buffer);
var sb = new StringBuilder();
for (int j = 0; j < i; j++)
{
char c = (char)((j % 10) + 10112);
sb.Append(c.ToString()); // incrementing unicode numbers, repeating
}
var s = sb.ToString();
output.WriteString(s);

output.Flush();

// Verify written content
var input = new CodedInputStream(buffer);

Assert.AreEqual(s, input.ReadString());
}
}
}
}
62 changes: 31 additions & 31 deletions csharp/src/Google.Protobuf/WritingPrimitives.cs
Expand Up @@ -194,39 +194,20 @@ public static void WriteString(ref Span<byte> buffer, ref WriterInternalState st
{
if (length == value.Length) // Must be all ASCII...
{
// If 64bit, and value has at least 4 chars, process 4 chars at a time.
if (IntPtr.Size == 8 && value.Length >= 4)
{
ref char sourceChars = ref MemoryMarshal.GetReference(value.AsSpan());
ref byte sourceBytes = ref Unsafe.As<char, byte>(ref sourceChars);
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
{
NarrowFourUtf16CharsToAsciiAndWriteToBuffer(
ref Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex),
Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref sourceBytes, (IntPtr)(currentIndex * 2))));

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

// Process 3 chars or less remainder. We already have refs to source and destination
// so use them to perform array operations without bounds checks.
for (; currentIndex < length; currentIndex++)
{
Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex) = (byte)Unsafe.AddByteOffset(ref sourceChars, (IntPtr)(currentIndex * 2));
}
}
else
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;

// This avoids bound checks
for (; currentIndex < length; currentIndex++)
{
for (int i = 0; i < length; i++)
{
buffer[state.position + i] = (byte)value[i];
}
Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex) = (byte)Unsafe.AddByteOffset(ref sourceChars, (IntPtr)(currentIndex * 2));
}

state.position += length;
}
else
Expand All @@ -245,6 +226,25 @@ 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)
{
ref byte sourceBytes = ref Unsafe.As<char, byte>(ref sourceChars);

// 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
{
NarrowFourUtf16CharsToAsciiAndWriteToBuffer(
ref Unsafe.AddByteOffset(ref destinationBytes, (IntPtr)currentIndex),
Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref sourceBytes, (IntPtr)(currentIndex * 2))));

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

return currentIndex;
}

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

0 comments on commit d4df924

Please sign in to comment.