Skip to content

Commit d4e88ca

Browse files
committed
Merge branch 'lukebakken/string-types' into stringallocations
2 parents 2320480 + 14ff1b0 commit d4e88ca

File tree

11 files changed

+319
-88
lines changed

11 files changed

+319
-88
lines changed

projects/RabbitMQ.Client/PublicAPI.Unshipped.txt

Lines changed: 24 additions & 9 deletions
Large diffs are not rendered by default.
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// This source code is dual-licensed under the Apache License, version
2+
// 2.0, and the Mozilla Public License, version 2.0.
3+
//
4+
// The APL v2.0:
5+
//
6+
//---------------------------------------------------------------------------
7+
// Copyright (c) 2007-2020 VMware, Inc.
8+
//
9+
// Licensed under the Apache License, Version 2.0 (the "License");
10+
// you may not use this file except in compliance with the License.
11+
// You may obtain a copy of the License at
12+
//
13+
// https://www.apache.org/licenses/LICENSE-2.0
14+
//
15+
// Unless required by applicable law or agreed to in writing, software
16+
// distributed under the License is distributed on an "AS IS" BASIS,
17+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
// See the License for the specific language governing permissions and
19+
// limitations under the License.
20+
//---------------------------------------------------------------------------
21+
//
22+
// The MPL v2.0:
23+
//
24+
//---------------------------------------------------------------------------
25+
// This Source Code Form is subject to the terms of the Mozilla Public
26+
// License, v. 2.0. If a copy of the MPL was not distributed with this
27+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
28+
//
29+
// Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
30+
//---------------------------------------------------------------------------
31+
32+
using System;
33+
using System.Text;
34+
using System.Text.RegularExpressions;
35+
36+
namespace RabbitMQ.Client
37+
{
38+
public abstract class AmqpString
39+
{
40+
private readonly string _value;
41+
private readonly ReadOnlyMemory<byte> _stringBytes;
42+
43+
public AmqpString()
44+
{
45+
_value = string.Empty;
46+
_stringBytes = ReadOnlyMemory<byte>.Empty;
47+
}
48+
49+
public AmqpString(string value, ushort maxLen, Encoding encoding)
50+
: this(value, maxLen, encoding, null)
51+
{
52+
}
53+
54+
public AmqpString(string value, ushort maxLen, Encoding encoding, string validatorRegex)
55+
{
56+
if (value.Length > maxLen)
57+
{
58+
throw new ArgumentOutOfRangeException(nameof(value));
59+
}
60+
61+
if (false == string.IsNullOrWhiteSpace(validatorRegex))
62+
{
63+
var re = new Regex(validatorRegex);
64+
if (false == re.IsMatch(value))
65+
{
66+
throw new ArgumentOutOfRangeException(nameof(value));
67+
}
68+
}
69+
70+
if (encoding == Encoding.ASCII)
71+
{
72+
if (false == isAscii(value))
73+
{
74+
throw new ArgumentOutOfRangeException(nameof(value));
75+
}
76+
}
77+
78+
_value = value;
79+
_stringBytes = new ReadOnlyMemory<byte>(encoding.GetBytes(value));
80+
}
81+
82+
public override string ToString()
83+
{
84+
return _value;
85+
}
86+
87+
public static implicit operator string(AmqpString amqpString)
88+
{
89+
return amqpString._value;
90+
}
91+
92+
public static implicit operator ReadOnlyMemory<byte>(AmqpString amqpString)
93+
{
94+
return amqpString._stringBytes;
95+
}
96+
97+
private bool isAscii(string value)
98+
{
99+
return Encoding.UTF8.GetByteCount(value) == value.Length;
100+
}
101+
}
102+
103+
/*
104+
* From the spec:
105+
* <domain name="exchange-name" type="shortstr" label="exchange name">
106+
* <doc> The exchange name is a client-selected string that identifies the exchange for publish methods. </doc>
107+
* <assert check="length" value="127"/>
108+
* <assert check="regexp" value="^[a-zA-Z0-9-_.:]*$"/>
109+
* </domain>
110+
*/
111+
public class ExchangeName : AmqpString
112+
{
113+
public static readonly ExchangeName Empty = new ExchangeName();
114+
115+
public ExchangeName() : base()
116+
{
117+
}
118+
119+
public ExchangeName(string exchangeName)
120+
: base(exchangeName, 127, Encoding.ASCII, "^[a-zA-Z0-9-_.:]*$")
121+
{
122+
}
123+
124+
public static explicit operator ExchangeName(string value)
125+
{
126+
return new ExchangeName(value);
127+
}
128+
}
129+
130+
/*
131+
* From the spec:
132+
* <domain name="queue-name" type="shortstr" label="queue name">
133+
* <doc> The queue name identifies the queue within the vhost. In methods where the queue name may be blank, and that has no specific significance, this refers to the 'current' queue for the channel, meaning the last queue that the client declared on the channel. If the client did not declare a queue, and the method needs a queue name, this will result in a 502 (syntax error) channel exception. </doc>
134+
* <assert check="length" value="127"/>
135+
* <assert check="regexp" value="^[a-zA-Z0-9-_.:]*$"/>
136+
* </domain>
137+
*/
138+
public class QueueName : AmqpString
139+
{
140+
public static readonly QueueName Empty = new QueueName();
141+
142+
public QueueName() : base()
143+
{
144+
}
145+
146+
public QueueName(string exchangeName)
147+
: base(exchangeName, 127, Encoding.ASCII, "^[a-zA-Z0-9-_.:]*$")
148+
{
149+
}
150+
151+
public static explicit operator QueueName(string value)
152+
{
153+
return new QueueName(value);
154+
}
155+
}
156+
157+
/*
158+
* From the spec:
159+
* <field name="routing-key" domain="shortstr" label="Message routing key">
160+
* <doc> Specifies the routing key for the message. The routing key is used for routing messages depending on the exchange configuration. </doc>
161+
* </field>
162+
* <domain name = "shortstr" type="shortstr" label="short string (max. 256 characters)"/>
163+
*/
164+
public class RoutingKey : AmqpString
165+
{
166+
public static readonly RoutingKey Empty = new RoutingKey();
167+
168+
public RoutingKey() : base()
169+
{
170+
}
171+
172+
public RoutingKey(string exchangeName)
173+
: base(exchangeName, 256, Encoding.ASCII)
174+
{
175+
}
176+
177+
public static explicit operator RoutingKey(string value)
178+
{
179+
return new RoutingKey(value);
180+
}
181+
}
182+
}

projects/RabbitMQ.Client/client/api/CachedString.cs

Lines changed: 0 additions & 63 deletions
This file was deleted.

projects/RabbitMQ.Client/client/api/IChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ ValueTask BasicPublishAsync<TProperties>(string exchange, string routingKey, TPr
224224
/// <remarks>
225225
/// Routing key must be shorter than 255 bytes.
226226
/// </remarks>
227-
ValueTask BasicPublishAsync<TProperties>(CachedString exchange, CachedString routingKey, TProperties basicProperties,
227+
ValueTask BasicPublishAsync<TProperties>(ExchangeName exchange, RoutingKey routingKey, TProperties basicProperties,
228228
ReadOnlyMemory<byte> body = default, bool mandatory = false,
229229
CancellationToken cancellationToken = default)
230230
where TProperties : IReadOnlyBasicProperties, IAmqpHeader;

projects/RabbitMQ.Client/client/api/IChannelExtensions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,12 @@ public static ValueTask BasicPublishAsync<T>(this IChannel channel, PublicationA
9393
return channel.BasicPublishAsync(addr.ExchangeName, addr.RoutingKey, basicProperties, body);
9494
}
9595

96-
public static ValueTask BasicPublishAsync(this IChannel channel, string exchange, string routingKey, ReadOnlyMemory<byte> body = default, bool mandatory = false)
96+
public static ValueTask BasicPublishAsync(this IChannel channel, string exchange, string routingKey,
97+
ReadOnlyMemory<byte> body = default, bool mandatory = false)
9798
=> channel.BasicPublishAsync(exchange, routingKey, EmptyBasicProperty.Empty, body, mandatory);
9899

99-
public static ValueTask BasicPublishAsync(this IChannel channel, CachedString exchange, CachedString routingKey, ReadOnlyMemory<byte> body = default, bool mandatory = false)
100+
public static ValueTask BasicPublishAsync(this IChannel channel, ExchangeName exchange, RoutingKey routingKey,
101+
ReadOnlyMemory<byte> body = default, bool mandatory = false)
100102
=> channel.BasicPublishAsync(exchange, routingKey, EmptyBasicProperty.Empty, body, mandatory);
101103

102104
#nullable disable

projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ public ValueTask BasicPublishAsync<TProperties>(string exchange, string routingK
288288
where TProperties : IReadOnlyBasicProperties, IAmqpHeader
289289
=> InnerChannel.BasicPublishAsync(exchange, routingKey, basicProperties, body, mandatory, cancellationToken);
290290

291-
public ValueTask BasicPublishAsync<TProperties>(CachedString exchange, CachedString routingKey, TProperties basicProperties,
291+
public ValueTask BasicPublishAsync<TProperties>(ExchangeName exchange, RoutingKey routingKey, TProperties basicProperties,
292292
ReadOnlyMemory<byte> body, bool mandatory,
293293
CancellationToken cancellationToken)
294294
where TProperties : IReadOnlyBasicProperties, IAmqpHeader

projects/RabbitMQ.Client/client/impl/ChannelBase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
11221122
}
11231123
}
11241124

1125-
public async ValueTask BasicPublishAsync<TProperties>(CachedString exchange, CachedString routingKey,
1125+
public async ValueTask BasicPublishAsync<TProperties>(ExchangeName exchange, RoutingKey routingKey,
11261126
TProperties basicProperties, ReadOnlyMemory<byte> body, bool mandatory,
11271127
CancellationToken cancellationToken)
11281128
where TProperties : IReadOnlyBasicProperties, IAmqpHeader
@@ -1143,11 +1143,11 @@ await _confirmSemaphore.WaitAsync(cancellationToken)
11431143

11441144
try
11451145
{
1146-
var cmd = new BasicPublishMemory(exchange.Bytes, routingKey.Bytes, mandatory, default);
1146+
var cmd = new BasicPublishMemory(exchange, routingKey, mandatory, default);
11471147

11481148
RabbitMQActivitySource.TryGetExistingContext(basicProperties, out ActivityContext existingContext);
11491149
using Activity sendActivity = RabbitMQActivitySource.PublisherHasListeners
1150-
? RabbitMQActivitySource.Send(routingKey.Value, exchange.Value, body.Length, existingContext)
1150+
? RabbitMQActivitySource.Send(routingKey, exchange, body.Length, existingContext)
11511151
: default;
11521152

11531153
if (sendActivity != null)

projects/Test/Integration/TestBasicPublish.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ public async Task TestBasicRoundtripCachedString()
9090
_conn = await _connFactory.CreateConnectionAsync();
9191
_channel = await _conn.CreateChannelAsync();
9292

93-
CachedString exchangeName = new CachedString(string.Empty);
94-
CachedString queueName = new CachedString((await _channel.QueueDeclareAsync()).QueueName);
93+
var exchangeName = new ExchangeName(string.Empty);
94+
var queueName = new QueueName((await _channel.QueueDeclareAsync()).QueueName);
9595
byte[] sendBody = _encoding.GetBytes("hi");
9696
byte[] consumeBody = null;
9797
var consumer = new EventingBasicConsumer(_channel);
@@ -102,7 +102,7 @@ public async Task TestBasicRoundtripCachedString()
102102
consumeBody = a.Body.ToArray();
103103
consumerReceivedSemaphore.Release();
104104
};
105-
string tag = await _channel.BasicConsumeAsync(queueName.Value, true, consumer);
105+
string tag = await _channel.BasicConsumeAsync(queueName, true, consumer);
106106

107107
await _channel.BasicPublishAsync(exchangeName, queueName, sendBody);
108108
bool waitResFalse = await consumerReceivedSemaphore.WaitAsync(TimeSpan.FromSeconds(2));

projects/Test/Integration/TestFloodPublishing.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public async Task TestUnthrottledFloodPublishing()
102102
}
103103
}
104104

105-
await _channel.BasicPublishAsync(CachedString.Empty, CachedString.Empty, _body);
105+
await _channel.BasicPublishAsync(ExchangeName.Empty, RoutingKey.Empty, _body);
106106
}
107107
}
108108
finally

projects/Test/Integration/TestPublishSharedChannelAsync.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ namespace Test.Integration
4040
public class TestPublishSharedChannelAsync : IntegrationFixture
4141
{
4242
private const string QueueName = "TestPublishSharedChannel_Queue";
43-
private static readonly CachedString ExchangeName = new CachedString("TestPublishSharedChannel_Ex");
44-
private static readonly CachedString PublishKey = new CachedString("TestPublishSharedChannel_RoutePub");
43+
private static readonly ExchangeName s_exchangeName = new ExchangeName("TestPublishSharedChannel_Ex");
44+
private static readonly RoutingKey s_publishKey = new RoutingKey("TestPublishSharedChannel_RoutePub");
4545
private const int Loops = 20;
4646
private const int Repeats = 1000;
4747

@@ -85,16 +85,16 @@ public async Task MultiThreadPublishOnSharedChannel()
8585
try
8686
{
8787
channel.ChannelShutdown += HandleChannelShutdown;
88-
await channel.ExchangeDeclareAsync(ExchangeName.Value, ExchangeType.Topic, passive: false, durable: false, autoDelete: true,
88+
await channel.ExchangeDeclareAsync(s_exchangeName, ExchangeType.Topic, passive: false, durable: false, autoDelete: true,
8989
noWait: false, arguments: null);
9090
await channel.QueueDeclareAsync(QueueName, exclusive: false, autoDelete: true);
91-
await channel.QueueBindAsync(QueueName, ExchangeName.Value, PublishKey.Value);
91+
await channel.QueueBindAsync(QueueName, s_exchangeName, s_publishKey);
9292

9393
for (int i = 0; i < Loops; i++)
9494
{
9595
for (int j = 0; j < Repeats; j++)
9696
{
97-
await channel.BasicPublishAsync(ExchangeName, PublishKey, _body, false);
97+
await channel.BasicPublishAsync(s_exchangeName, s_publishKey, _body, false);
9898
}
9999
}
100100
}

0 commit comments

Comments
 (0)