Merge pull request #19511 from mgravell/mgravell/unsafe-encode

csharp remove byte[] allocations during UTF8 encode/decode
pull/19568/head
Jan Tattermusch 6 years ago committed by GitHub
commit 70dd0b9b3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj
  2. 25
      src/csharp/Grpc.Core.Api/Metadata.cs
  3. 54
      src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs
  4. 3
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  5. 62
      src/csharp/Grpc.Core.Tests/Internal/WellKnownStringsTest.cs
  6. 30
      src/csharp/Grpc.Core.Tests/MetadataTest.cs
  7. 2
      src/csharp/Grpc.Core/Grpc.Core.csproj
  8. 46
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  9. 38
      src/csharp/Grpc.Core/Internal/MarshalUtils.cs
  10. 12
      src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
  11. 6
      src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs
  12. 92
      src/csharp/Grpc.Core/Internal/WellKnownStrings.cs
  13. 2
      src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs
  14. 1
      src/csharp/tests.json
  15. 4
      templates/src/csharp/Grpc.Core/Internal/native_methods.include

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Grpc.Core\Common.csproj.include" />
@ -11,6 +11,7 @@
<PackageProjectUrl>https://github.com/grpc/grpc</PackageProjectUrl>
<PackageTags>gRPC RPC HTTP/2</PackageTags>
<VersionPrefix>$(GrpcCsharpVersion)</VersionPrefix>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>

@ -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.
/// </summary>
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");

@ -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
/// <summary>
/// Converts <c>byte*</c> pointing to an encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
/// </summary>
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
/// <summary>
/// Converts <c>IntPtr</c> pointing to a encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe string GetString(this Encoding encoding, IntPtr ptr, int len)
{
return len == 0 ? "" : encoding.GetString((byte*)ptr.ToPointer(), len);
}
}
}

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Grpc.Core\Common.csproj.include" />
@ -6,6 +6,7 @@
<TargetFrameworks>net45;netcoreapp2.1</TargetFrameworks>
<OutputType>Exe</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">

@ -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);
}
}
}
}
}

@ -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]

@ -100,6 +100,8 @@
<ItemGroup>
<PackageReference Include="System.Interactive.Async" Version="3.2.0" />
<!-- System.Buffers *may* come in transitively, but: we can *always* use ArrayPool -->
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

@ -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<byte>.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<byte>.Shared.Return(statusBuffer);
}
}
}
}
}

@ -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
/// <summary>
/// Converts <c>IntPtr</c> pointing to a UTF-8 encoded byte array to <c>string</c>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string PtrToStringUTF8(IntPtr ptr, int len)
{
if (len == 0)
return EncodingUTF8.GetString(ptr, len);
}
/// <summary>
/// UTF-8 encodes the given string into a buffer of sufficient size
/// </summary>
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);
}
/// <summary>
/// Returns byte array containing UTF-8 encoding of given string.
/// Returns the maximum number of bytes required to encode a given string.
/// </summary>
public static byte[] GetBytesUTF8(string str)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetMaxByteCountUTF8(string str)
{
return EncodingUTF8.GetBytes(str);
return EncodingUTF8.GetMaxByteCount(str.Length);
}
/// <summary>
/// Get string from a UTF8 encoded byte array.
/// Returns the actual number of bytes required to encode a given string.
/// </summary>
public static string GetStringUTF8(byte[] bytes)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetByteCountUTF8(string str)
{
return EncodingUTF8.GetString(bytes);
return EncodingUTF8.GetByteCount(str);
}
}
}

@ -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;
}

@ -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);

@ -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
{
/// <summary>
/// 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)
/// </summary>
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;
}
/// <summary>
/// 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
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe string TryIdentify(IntPtr source, int length)
{
return TryIdentify((byte*)source.ToPointer(), length);
}
/// <summary>
/// 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
/// </summary>
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;
}
}
}

@ -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;
};

@ -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",

@ -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)
%></%def>
%></%def>

Loading…
Cancel
Save