Merge pull request #16631 from szehetner/Allocations

C#: Various allocation optimizations
pull/15223/head
Jan Tattermusch 6 years ago committed by GitHub
commit 5a34293615
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      src/csharp/Grpc.Core.Tests/MetadataTest.cs
  2. 20
      src/csharp/Grpc.Core/ClientBase.cs
  3. 7
      src/csharp/Grpc.Core/Internal/MarshalUtils.cs
  4. 40
      src/csharp/Grpc.Core/Metadata.cs

@ -72,6 +72,23 @@ namespace Grpc.Core.Tests
Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc/", "xyz"));
}
[Test]
public void KeysAreNormalized_UppercaseKey()
{
var uppercaseKey = "ABC";
var entry = new Metadata.Entry(uppercaseKey, "XYZ");
Assert.AreEqual("abc", entry.Key);
}
[Test]
public void KeysAreNormalized_LowercaseKey()
{
var lowercaseKey = "abc";
var entry = new Metadata.Entry(lowercaseKey, "XYZ");
// no allocation if key already lowercase
Assert.AreSame(lowercaseKey, entry.Key);
}
[Test]
public void Entry_ConstructionPreconditions()
{

@ -151,12 +151,12 @@ namespace Grpc.Core
{
private class ClientBaseConfigurationInterceptor : Interceptor
{
readonly Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor;
readonly Func<IMethod, string, CallOptions, ClientBaseConfigurationInfo> interceptor;
/// <summary>
/// Creates a new instance of ClientBaseConfigurationInterceptor given the specified header and host interceptor function.
/// </summary>
public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor)
public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, ClientBaseConfigurationInfo> interceptor)
{
this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
}
@ -166,7 +166,7 @@ namespace Grpc.Core
where TResponse : class
{
var newHostAndCallOptions = interceptor(context.Method, context.Host, context.Options);
return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Item1, newHostAndCallOptions.Item2);
return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Host, newHostAndCallOptions.CallOptions);
}
public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
@ -195,6 +195,18 @@ namespace Grpc.Core
}
}
internal struct ClientBaseConfigurationInfo
{
internal readonly string Host;
internal readonly CallOptions CallOptions;
internal ClientBaseConfigurationInfo(string host, CallOptions callOptions)
{
Host = host;
CallOptions = callOptions;
}
}
readonly CallInvoker undecoratedCallInvoker;
readonly string host;
@ -206,7 +218,7 @@ namespace Grpc.Core
internal CallInvoker CreateDecoratedCallInvoker()
{
return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => Tuple.Create(this.host, options)));
return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => new ClientBaseConfigurationInfo(this.host, options)));
}
internal ClientBaseConfiguration WithHost(string host)

@ -35,6 +35,13 @@ namespace Grpc.Core.Internal
/// </summary>
public static string PtrToStringUTF8(IntPtr ptr, int len)
{
if (len == 0)
{
return "";
}
// 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);

@ -225,8 +225,6 @@ namespace Grpc.Core
/// </summary>
public class Entry
{
private static readonly Regex ValidKeyRegex = new Regex("^[.a-z0-9_-]+$");
readonly string key;
readonly string value;
readonly byte[] valueBytes;
@ -358,10 +356,42 @@ namespace Grpc.Core
private static string NormalizeKey(string key)
{
var normalized = GrpcPreconditions.CheckNotNull(key, "key").ToLowerInvariant();
GrpcPreconditions.CheckArgument(ValidKeyRegex.IsMatch(normalized),
GrpcPreconditions.CheckNotNull(key, "key");
GrpcPreconditions.CheckArgument(IsValidKey(key, out bool isLowercase),
"Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores, hyphens and dots.");
return normalized;
if (isLowercase)
{
// save allocation of a new string if already lowercase
return key;
}
return key.ToLowerInvariant();
}
private static bool IsValidKey(string input, out bool isLowercase)
{
isLowercase = true;
for (int i = 0; i < input.Length; i++)
{
char c = input[i];
if ('a' <= c && c <= 'z' ||
'0' <= c && c <= '9' ||
c == '.' ||
c == '_' ||
c == '-' )
continue;
if ('A' <= c && c <= 'Z')
{
isLowercase = false;
continue;
}
return false;
}
return true;
}
/// <summary>

Loading…
Cancel
Save