diff --git a/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj b/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj index 8a32bc757df..0a958299ec8 100755 --- a/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj +++ b/src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj @@ -1,4 +1,4 @@ - + @@ -11,6 +11,7 @@ https://github.com/grpc/grpc gRPC RPC HTTP/2 $(GrpcCsharpVersion) + true diff --git a/src/csharp/Grpc.Core.Api/Metadata.cs b/src/csharp/Grpc.Core.Api/Metadata.cs index 27e72fbfa8b..f7f0ae1c93a 100644 --- a/src/csharp/Grpc.Core.Api/Metadata.cs +++ b/src/csharp/Grpc.Core.Api/Metadata.cs @@ -17,8 +17,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; -using System.Text.RegularExpressions; +using Grpc.Core.Api.Utils; using Grpc.Core.Utils; @@ -345,15 +346,31 @@ namespace Grpc.Core /// Creates a binary value or ascii value metadata entry from data received from the native layer. /// We trust C core to give us well-formed data, so we don't perform any checks or defensive copying. /// - internal static Entry CreateUnsafe(string key, byte[] valueBytes) + internal static Entry CreateUnsafe(string key, IntPtr source, int length) { if (HasBinaryHeaderSuffix(key)) { - return new Entry(key, null, valueBytes); + byte[] arr; + if (length == 0) + { + arr = EmptyByteArray; + } + else + { // create a local copy in a fresh array + arr = new byte[length]; + Marshal.Copy(source, arr, 0, length); + } + return new Entry(key, null, arr); + } + else + { + string s = EncodingASCII.GetString(source, length); + return new Entry(key, s, null); } - return new Entry(key, EncodingASCII.GetString(valueBytes), null); } + static readonly byte[] EmptyByteArray = new byte[0]; + private static string NormalizeKey(string key) { GrpcPreconditions.CheckNotNull(key, "key"); diff --git a/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs new file mode 100644 index 00000000000..080fbcd5c12 --- /dev/null +++ b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs @@ -0,0 +1,54 @@ +#region Copyright notice and license +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Grpc.Core.Api.Utils +{ + + internal static class EncodingExtensions + { +#if NET45 // back-fill over a method missing in NET45 + /// + /// Converts byte* pointing to an encoded byte array to a string using the provided Encoding. + /// + public static unsafe string GetString(this Encoding encoding, byte* source, int byteCount) + { + if (byteCount == 0) return ""; // most callers will have already checked, but: make sure + + // allocate a right-sized string and decode into it + int charCount = encoding.GetCharCount(source, byteCount); + string s = new string('\0', charCount); + fixed (char* cPtr = s) + { + encoding.GetChars(source, byteCount, cPtr, charCount); + } + return s; + } +#endif + /// + /// Converts IntPtr pointing to a encoded byte array to a string using the provided Encoding. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe string GetString(this Encoding encoding, IntPtr ptr, int len) + { + return len == 0 ? "" : encoding.GetString((byte*)ptr.ToPointer(), len); + } + } + +} diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 7fef2c77091..693646bea1c 100755 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -6,6 +6,7 @@ net45;netcoreapp2.1 Exe true + true diff --git a/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs b/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs new file mode 100644 index 00000000000..4c89b993238 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs @@ -0,0 +1,62 @@ +#region Copyright notice and license +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +using System.Text; +using Grpc.Core.Internal; +using NUnit.Framework; + +namespace Grpc.Core.Internal.Tests +{ + public class WellKnownStringsTest + { + [Test] + [TestCase("", true)] + [TestCase("u", false)] + [TestCase("us", false)] + [TestCase("use", false)] + [TestCase("user", false)] + [TestCase("user-", false)] + [TestCase("user-a", false)] + [TestCase("user-ag", false)] + [TestCase("user-age", false)] + [TestCase("user-agent", true)] + [TestCase("user-agent ", false)] + [TestCase("useragent ", false)] + [TestCase("User-Agent", false)] + [TestCase("sdlkfjlskjfdlkjs;lfdksflsdfkh skjdfh sdkfhskdhf skjfhk sdhjkjh", false)] + + // test for endianness snafus (reversed in segments) + [TestCase("ega-resutn", false)] + public unsafe void TestWellKnownStrings(string input, bool expected) + { + // create a copy of the data; no cheating! + byte[] bytes = Encoding.ASCII.GetBytes(input); + fixed(byte* ptr = bytes) + { + string result = WellKnownStrings.TryIdentify(ptr, bytes.Length); + if (expected) Assert.AreEqual(input, result); + else Assert.IsNull(result); + + if (expected) + { + // try again, and check we get the same instance + string again = WellKnownStrings.TryIdentify(ptr, bytes.Length); + Assert.AreSame(result, again); + } + } + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index d85d7572a63..0b5f1c76e0c 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -111,25 +111,31 @@ namespace Grpc.Core.Tests } [Test] - public void Entry_CreateUnsafe_Ascii() + public unsafe void Entry_CreateUnsafe_Ascii() { var bytes = new byte[] { (byte)'X', (byte)'y' }; - var entry = Metadata.Entry.CreateUnsafe("abc", bytes); - Assert.IsFalse(entry.IsBinary); - Assert.AreEqual("abc", entry.Key); - Assert.AreEqual("Xy", entry.Value); - CollectionAssert.AreEqual(bytes, entry.ValueBytes); + fixed (byte* ptr = bytes) + { + var entry = Metadata.Entry.CreateUnsafe("abc", new IntPtr(ptr), bytes.Length); + Assert.IsFalse(entry.IsBinary); + Assert.AreEqual("abc", entry.Key); + Assert.AreEqual("Xy", entry.Value); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } } [Test] - public void Entry_CreateUnsafe_Binary() + public unsafe void Entry_CreateUnsafe_Binary() { var bytes = new byte[] { 1, 2, 3 }; - var entry = Metadata.Entry.CreateUnsafe("abc-bin", bytes); - Assert.IsTrue(entry.IsBinary); - Assert.AreEqual("abc-bin", entry.Key); - Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); - CollectionAssert.AreEqual(bytes, entry.ValueBytes); + fixed (byte* ptr = bytes) + { + var entry = Metadata.Entry.CreateUnsafe("abc-bin", new IntPtr(ptr), bytes.Length); + Assert.IsTrue(entry.IsBinary); + Assert.AreEqual("abc-bin", entry.Key); + Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); + CollectionAssert.AreEqual(bytes, entry.ValueBytes); + } } [Test] diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index afd60e73a21..1844ce335bc 100755 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -100,6 +100,8 @@ + + diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs index 858d2a69605..2e637c9704b 100644 --- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs @@ -21,6 +21,7 @@ using System.Text; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Core.Profiling; +using System.Buffers; namespace Grpc.Core.Internal { @@ -134,9 +135,48 @@ namespace Grpc.Core.Internal { var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendStatusFromServerCompletionCallback, callback); var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero; - var statusDetailBytes = MarshalUtils.GetBytesUTF8(status.Detail); - Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, statusDetailBytes, new UIntPtr((ulong)statusDetailBytes.Length), metadataArray, sendEmptyInitialMetadata ? 1 : 0, - optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + + const int MaxStackAllocBytes = 256; + int maxBytes = MarshalUtils.GetMaxByteCountUTF8(status.Detail); + if (maxBytes > MaxStackAllocBytes) + { + // pay the extra to get the *actual* size; this could mean that + // it ends up fitting on the stack after all, but even if not + // it will mean that we ask for a *much* smaller buffer + maxBytes = MarshalUtils.GetByteCountUTF8(status.Detail); + } + + unsafe + { + if (maxBytes <= MaxStackAllocBytes) + { // for small status, we can encode on the stack without touching arrays + // note: if init-locals is disabled, it would be more efficient + // to just stackalloc[MaxStackAllocBytes]; but by default, since we + // expect this to be small and it needs to wipe, just use maxBytes + byte* ptr = stackalloc byte[maxBytes]; + int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes); + Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0, + optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + } + else + { // for larger status (rare), rent a buffer from the pool and + // use that for encoding + var statusBuffer = ArrayPool.Shared.Rent(maxBytes); + try + { + fixed (byte* ptr = statusBuffer) + { + int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes); + Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0, + optionalPayload, optionalPayloadLength, writeFlags).CheckOk(); + } + } + finally + { + ArrayPool.Shared.Return(statusBuffer); + } + } + } } } diff --git a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs index e3e41617955..313e2b5c9d7 100644 --- a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs +++ b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs @@ -17,8 +17,9 @@ #endregion using System; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using System.Text; +using Grpc.Core.Api.Utils; namespace Grpc.Core.Internal { @@ -32,34 +33,41 @@ namespace Grpc.Core.Internal /// /// Converts IntPtr pointing to a UTF-8 encoded byte array to string. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string PtrToStringUTF8(IntPtr ptr, int len) { - if (len == 0) + return EncodingUTF8.GetString(ptr, len); + } + + /// + /// UTF-8 encodes the given string into a buffer of sufficient size + /// + public static unsafe int GetBytesUTF8(string str, byte* destination, int destinationLength) + { + int charCount = str.Length; + if (charCount == 0) return 0; + fixed (char* source = str) { - return ""; + return EncodingUTF8.GetBytes(source, charCount, destination, destinationLength); } - - // TODO(jtattermusch): once Span dependency is added, - // use Span-based API to decode the string without copying the buffer. - var bytes = new byte[len]; - Marshal.Copy(ptr, bytes, 0, len); - return EncodingUTF8.GetString(bytes); } /// - /// Returns byte array containing UTF-8 encoding of given string. + /// Returns the maximum number of bytes required to encode a given string. /// - public static byte[] GetBytesUTF8(string str) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetMaxByteCountUTF8(string str) { - return EncodingUTF8.GetBytes(str); + return EncodingUTF8.GetMaxByteCount(str.Length); } /// - /// Get string from a UTF8 encoded byte array. + /// Returns the actual number of bytes required to encode a given string. /// - public static string GetStringUTF8(byte[] bytes) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetByteCountUTF8(string str) { - return EncodingUTF8.GetString(bytes); + return EncodingUTF8.GetByteCount(str); } } } diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs index 13fde68fe0f..3edfbfa3bfd 100644 --- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs @@ -15,8 +15,7 @@ #endregion using System; using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Grpc.Core.Profiling; +using System.Text; namespace Grpc.Core.Internal { @@ -66,12 +65,13 @@ namespace Grpc.Core.Internal var index = new UIntPtr(i); UIntPtr keyLen; IntPtr keyPtr = Native.grpcsharp_metadata_array_get_key(metadataArray, index, out keyLen); - string key = Marshal.PtrToStringAnsi(keyPtr, (int)keyLen.ToUInt32()); + int keyLen32 = checked((int)keyLen.ToUInt32()); + string key = WellKnownStrings.TryIdentify(keyPtr, keyLen32) + ?? Marshal.PtrToStringAnsi(keyPtr, keyLen32); UIntPtr valueLen; IntPtr valuePtr = Native.grpcsharp_metadata_array_get_value(metadataArray, index, out valueLen); - var bytes = new byte[valueLen.ToUInt64()]; - Marshal.Copy(valuePtr, bytes, 0, bytes.Length); - metadata.Add(Metadata.Entry.CreateUnsafe(key, bytes)); + int len32 = checked((int)valueLen.ToUInt64()); + metadata.Add(Metadata.Entry.CreateUnsafe(key, valuePtr, len32)); } return metadata; } diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs index b8a60b31c40..170fc5c5657 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -469,7 +469,7 @@ namespace Grpc.Core.Internal public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); @@ -637,7 +637,7 @@ namespace Grpc.Core.Internal public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); [DllImport(ImportName)] - public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); [DllImport(ImportName)] public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); @@ -933,7 +933,7 @@ namespace Grpc.Core.Internal public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); [DllImport(ImportName)] - public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); [DllImport(ImportName)] public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); diff --git a/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs b/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs new file mode 100644 index 00000000000..fa33e9a1fe4 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/WellKnownStrings.cs @@ -0,0 +1,92 @@ +#region Copyright notice and license +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +using System; +using System.Runtime.CompilerServices; + +namespace Grpc.Core.Internal +{ + /// + /// Utility type for identifying "well-known" strings (i.e. headers/keys etc that + /// we expect to see frequently, and don't want to allocate lots of copies of) + /// + internal static class WellKnownStrings + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong Coerce64(byte* value) + { + return *(ulong*)value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint Coerce32(byte* value) + { + return *(uint*)value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ushort Coerce16(byte* value) + { + return *(ushort*)value; + } + + + /// + /// Test whether the provided byte sequence is recognized as a well-known string; if + /// so, return a shared instance of that string; otherwise, return null + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe string TryIdentify(IntPtr source, int length) + { + return TryIdentify((byte*)source.ToPointer(), length); + } + + /// + /// Test whether the provided byte sequence is recognized as a well-known string; if + /// so, return a shared instance of that string; otherwise, return null + /// + public static unsafe string TryIdentify(byte* source, int length) + { + // note: the logic here is hard-coded to constants for optimal processing; + // refer to an ASCII/hex converter (and remember to reverse **segments** for little-endian) + if (BitConverter.IsLittleEndian) // this is a JIT intrinsic; branch removal happens on modern runtimes + { + switch (length) + { + case 0: return ""; + case 10: + switch(Coerce64(source)) + { + case 0x6567612d72657375: return Coerce16(source + 8) == 0x746e ? "user-agent" : null; + } + break; + } + } + else + { + switch (length) + { + case 0: return ""; + case 10: + switch (Coerce64(source)) + { + case 0x757365722d616765: return Coerce16(source + 8) == 0x6e74 ? "user-agent" : null; + } + break; + } + } + return null; + } + } +} diff --git a/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs b/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs index 3eddb33788a..a59afa19118 100644 --- a/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs +++ b/src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs @@ -61,7 +61,7 @@ namespace Grpc.Microbenchmarks var native = NativeMethods.Get(); // nop the native-call via reflection - NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => { + NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => { completionRegistry.Extract(ctx.Handle).OnComplete(true); // drain the dictionary as we go return CallError.OK; }; diff --git a/src/csharp/tests.json b/src/csharp/tests.json index cacdb305d2e..2eb786e0983 100644 --- a/src/csharp/tests.json +++ b/src/csharp/tests.json @@ -14,6 +14,7 @@ "Grpc.Core.Internal.Tests.ReusableSliceBufferTest", "Grpc.Core.Internal.Tests.SliceTest", "Grpc.Core.Internal.Tests.TimespecTest", + "Grpc.Core.Internal.Tests.WellKnownStringsTest", "Grpc.Core.Tests.AppDomainUnloadTest", "Grpc.Core.Tests.AuthContextTest", "Grpc.Core.Tests.AuthPropertyTest", diff --git a/templates/src/csharp/Grpc.Core/Internal/native_methods.include b/templates/src/csharp/Grpc.Core/Internal/native_methods.include index 4a31171aa27..ffcaf16bca9 100644 --- a/templates/src/csharp/Grpc.Core/Internal/native_methods.include +++ b/templates/src/csharp/Grpc.Core/Internal/native_methods.include @@ -31,7 +31,7 @@ native_method_signatures = [ 'CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)', 'CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata)', 'CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx)', - 'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags)', + 'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags)', 'CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx)', 'CallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx)', 'CallError grpcsharp_call_start_serverside(CallSafeHandle call, BatchContextSafeHandle ctx)', @@ -107,4 +107,4 @@ for signature in native_method_signatures: native_methods.append({'returntype': match.group(1), 'name': match.group(2), 'params': match.group(3), 'comment': match.group(4)}) return list(native_methods) -%> \ No newline at end of file +%>