|
31 | 31 |
|
32 | 32 | using System; |
33 | 33 | using System.Buffers; |
| 34 | +using System.Buffers.Binary; |
34 | 35 | using System.IO; |
35 | 36 | using System.IO.Pipelines; |
36 | 37 | using System.Runtime.CompilerServices; |
37 | 38 | using System.Threading; |
| 39 | +using System.Threading.Channels; |
38 | 40 | using System.Threading.Tasks; |
39 | 41 | using RabbitMQ.Client.Exceptions; |
40 | 42 | using RabbitMQ.Client.Framing; |
@@ -113,17 +115,30 @@ internal static class BodySegment |
113 | 115 | * +--------------+ |
114 | 116 | * | x bytes | |
115 | 117 | * +--------------+ */ |
| 118 | + public const int HeaderSize = StartPayload; |
| 119 | + public const int FooterSize = 1; |
116 | 120 | public const int FrameSize = BaseFrameSize; |
117 | 121 |
|
118 | 122 | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
119 | 123 | public static int WriteTo(Span<byte> span, ushort channel, ReadOnlySpan<byte> body) |
120 | 124 | { |
121 | 125 | const int StartBodyArgument = StartPayload; |
122 | | - NetworkOrderSerializer.WriteUInt64(ref span.GetStart(), ((ulong)Constants.FrameBody << 56) | ((ulong)channel << 40) | ((ulong)body.Length << 8)); |
| 126 | + WriteHeader(span, channel, body.Length); |
123 | 127 | body.CopyTo(span.Slice(StartBodyArgument)); |
124 | 128 | span[StartPayload + body.Length] = Constants.FrameEnd; |
| 129 | + WriteFooter(span.Slice(StartPayload + body.Length)); |
125 | 130 | return body.Length + BaseFrameSize; |
126 | 131 | } |
| 132 | + |
| 133 | + public static void WriteHeader(Span<byte> span, ushort channel, int bodyLength) |
| 134 | + { |
| 135 | + NetworkOrderSerializer.WriteUInt64(ref span.GetStart(), ((ulong)Constants.FrameBody << 56) | ((ulong)channel << 40) | ((ulong)bodyLength << 8)); |
| 136 | + } |
| 137 | + |
| 138 | + public static void WriteFooter(Span<byte> span) |
| 139 | + { |
| 140 | + span[0] = Constants.FrameEnd; |
| 141 | + } |
127 | 142 | } |
128 | 143 |
|
129 | 144 | internal static class Heartbeat |
@@ -170,27 +185,42 @@ public static OutgoingFrameMemory SerializeToFrames<TMethod, THeader>(ref TMetho |
170 | 185 | where TMethod : struct, IOutgoingAmqpMethod |
171 | 186 | where THeader : IAmqpHeader |
172 | 187 | { |
173 | | - int remainingBodyBytes = body.Length; |
174 | | - int size = Method.FrameSize + Header.FrameSize + |
175 | | - method.GetRequiredBufferSize() + header.GetRequiredBufferSize() + |
176 | | - BodySegment.FrameSize * GetBodyFrameCount(maxBodyPayloadBytes, remainingBodyBytes) + remainingBodyBytes; |
177 | | - |
178 | 188 | // Will be returned by SocketFrameWriter.WriteLoop |
179 | | - IMemoryOwner<byte> buffer = MemoryPool<byte>.Shared.Rent(size); |
| 189 | + IMemoryOwner<byte> bodyCopy = MemoryPool<byte>.Shared.Rent(body.Length); |
| 190 | + body.CopyTo(bodyCopy.Memory); |
| 191 | + return SerializeToFrames(ref method, ref header, bodyCopy, body.Length, channelNumber, maxBodyPayloadBytes); |
| 192 | + } |
| 193 | + |
| 194 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| 195 | + public static OutgoingFrameMemory SerializeToFrames<TMethod, THeader>(ref TMethod method, ref THeader header, IMemoryOwner<byte> body, int bodyLength, ushort channelNumber, int maxBodyPayloadBytes) |
| 196 | + where TMethod : struct, IOutgoingAmqpMethod |
| 197 | + where THeader : IAmqpHeader |
| 198 | + { |
| 199 | + // Calculate ONLY the Method and Header framing size |
| 200 | + int framingSize = Method.FrameSize + Header.FrameSize + |
| 201 | + method.GetRequiredBufferSize() + header.GetRequiredBufferSize(); |
| 202 | + |
| 203 | + // Pre-calculate total final sequence size |
| 204 | + int bodyFramesCount = GetBodyFrameCount(maxBodyPayloadBytes, bodyLength); |
| 205 | + int totalSize = framingSize + bodyLength + (BodySegment.FrameSize * bodyFramesCount); |
| 206 | + |
| 207 | + // Rent a smaller buffer exclusively for the Method and Header |
| 208 | + IMemoryOwner<byte> buffer = MemoryPool<byte>.Shared.Rent(framingSize); |
180 | 209 | Span<byte> bufferSpan = buffer.Memory.Span; |
181 | 210 |
|
182 | 211 | int offset = Method.WriteTo(bufferSpan, channelNumber, ref method); |
183 | | - offset += Header.WriteTo(bufferSpan.Slice(offset), channelNumber, ref header, remainingBodyBytes); |
184 | | - ReadOnlySpan<byte> bodySpan = body.Span; |
185 | | - while (remainingBodyBytes > 0) |
186 | | - { |
187 | | - int frameSize = remainingBodyBytes > maxBodyPayloadBytes ? maxBodyPayloadBytes : remainingBodyBytes; |
188 | | - offset += BodySegment.WriteTo(bufferSpan.Slice(offset), channelNumber, bodySpan.Slice(bodySpan.Length - remainingBodyBytes, frameSize)); |
189 | | - remainingBodyBytes -= frameSize; |
190 | | - } |
191 | | - |
192 | | - System.Diagnostics.Debug.Assert(offset == size, $"Serialized to wrong size, expect {size}, offset {offset}"); |
193 | | - return new OutgoingFrameMemory(buffer, size); |
| 212 | + offset += Header.WriteTo(bufferSpan.Slice(offset), channelNumber, ref header, bodyLength); |
| 213 | + |
| 214 | + System.Diagnostics.Debug.Assert(offset == framingSize, $"Serialized to wrong size, expect {framingSize}, offset {offset}"); |
| 215 | + |
| 216 | + return new OutgoingFrameMemory( |
| 217 | + buffer, |
| 218 | + framingSize, |
| 219 | + body, |
| 220 | + bodyLength, |
| 221 | + channelNumber, |
| 222 | + maxBodyPayloadBytes, |
| 223 | + totalSize); |
194 | 224 | } |
195 | 225 |
|
196 | 226 | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
0 commit comments