1 // This source code is dual-licensed under the Apache License, version
2 // 2.0, and the Mozilla Public License, version 1.1.
6 //---------------------------------------------------------------------------
7 // Copyright (C) 2007-2009 LShift Ltd., Cohesive Financial
8 // Technologies LLC., and Rabbit Technologies Ltd.
10 // Licensed under the Apache License, Version 2.0 (the "License");
11 // you may not use this file except in compliance with the License.
12 // You may obtain a copy of the License at
14 // http://www.apache.org/licenses/LICENSE-2.0
16 // Unless required by applicable law or agreed to in writing, software
17 // distributed under the License is distributed on an "AS IS" BASIS,
18 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 // See the License for the specific language governing permissions and
20 // limitations under the License.
21 //---------------------------------------------------------------------------
25 //---------------------------------------------------------------------------
26 // The contents of this file are subject to the Mozilla Public License
27 // Version 1.1 (the "License"); you may not use this file except in
28 // compliance with the License. You may obtain a copy of the License at
29 // http://www.rabbitmq.com/mpl.html
31 // Software distributed under the License is distributed on an "AS IS"
32 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
33 // License for the specific language governing rights and limitations
36 // The Original Code is The RabbitMQ .NET Client.
38 // The Initial Developers of the Original Code are LShift Ltd,
39 // Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
41 // Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,
42 // Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd
43 // are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial
44 // Technologies LLC, and Rabbit Technologies Ltd.
46 // Portions created by LShift Ltd are Copyright (C) 2007-2009 LShift
47 // Ltd. Portions created by Cohesive Financial Technologies LLC are
48 // Copyright (C) 2007-2009 Cohesive Financial Technologies
49 // LLC. Portions created by Rabbit Technologies Ltd are Copyright
50 // (C) 2007-2009 Rabbit Technologies Ltd.
52 // All Rights Reserved.
54 // Contributor(s): ______________________________________.
56 //---------------------------------------------------------------------------
60 using System.Collections;
61 using RabbitMQ.Client;
62 using RabbitMQ.Client.Exceptions;
65 namespace RabbitMQ.Client.Impl
67 public class WireFormatting
69 public static byte ReadOctet(NetworkBinaryReader reader)
71 return reader.ReadByte();
74 public static string ReadShortstr(NetworkBinaryReader reader)
76 int byteCount = reader.ReadByte();
77 return Encoding.UTF8.GetString(reader.ReadBytes(byteCount));
80 public static byte[] ReadLongstr(NetworkBinaryReader reader)
82 uint byteCount = reader.ReadUInt32();
83 if (byteCount > int.MaxValue)
85 throw new SyntaxError("Long string too long; " +
86 "byte length=" + byteCount + ", max=" + int.MaxValue);
88 return reader.ReadBytes((int)byteCount);
91 public static ushort ReadShort(NetworkBinaryReader reader)
93 return reader.ReadUInt16();
96 public static uint ReadLong(NetworkBinaryReader reader)
98 return reader.ReadUInt32();
101 public static ulong ReadLonglong(NetworkBinaryReader reader)
103 return reader.ReadUInt64();
106 public static decimal AmqpToDecimal(byte scale, uint unsignedMantissa)
110 throw new SyntaxError("Unrepresentable AMQP decimal table field: " +
113 return new decimal((int)(unsignedMantissa & 0x7FFFFFFF),
116 ((unsignedMantissa & 0x80000000) == 0) ? false : true,
120 public static decimal ReadDecimal(NetworkBinaryReader reader)
122 byte scale = ReadOctet(reader);
123 uint unsignedMantissa = ReadLong(reader);
124 return AmqpToDecimal(scale, unsignedMantissa);
127 ///<summary>Reads an AMQP "table" definition from the reader.</summary>
129 /// Supports the AMQP 0-8/0-9 standard entry types S, I, D, T
130 /// and F, as well as the QPid-0-8 specific b, d, f, l, s, t,
133 public static IDictionary ReadTable(NetworkBinaryReader reader)
135 Hashtable table = new Hashtable();
136 long tableLength = reader.ReadUInt32();
138 Stream backingStream = reader.BaseStream;
139 long startPosition = backingStream.Position;
140 while ((backingStream.Position - startPosition) < tableLength)
142 string key = ReadShortstr(reader);
145 byte discriminator = reader.ReadByte();
146 switch ((char)discriminator)
149 value = ReadLongstr(reader);
152 value = reader.ReadInt32();
155 value = ReadDecimal(reader);
158 value = ReadTimestamp(reader);
161 value = ReadTable(reader);
165 value = ReadOctet(reader);
168 value = reader.ReadDouble();
171 value = reader.ReadSingle();
174 value = reader.ReadInt64();
177 value = reader.ReadInt16();
180 value = (ReadOctet(reader) != 0);
183 value = new BinaryTableValue(ReadLongstr(reader));
190 throw new SyntaxError("Unrecognised type in table: " +
191 (char) discriminator);
194 if (!table.ContainsKey(key))
203 public static AmqpTimestamp ReadTimestamp(NetworkBinaryReader reader)
205 ulong stamp = ReadLonglong(reader);
206 // 0-9 is afaict silent on the signedness of the timestamp.
207 // See also MethodArgumentWriter.WriteTimestamp and AmqpTimestamp itself
208 return new AmqpTimestamp((long)stamp);
211 public static void WriteOctet(NetworkBinaryWriter writer, byte val)
213 writer.Write((byte)val);
216 public static void WriteShortstr(NetworkBinaryWriter writer, string val)
218 byte[] bytes = Encoding.UTF8.GetBytes(val);
219 int len = bytes.Length;
222 throw new WireFormattingException("Short string too long; " +
223 "UTF-8 encoded length=" + len + ", max=255");
225 writer.Write((byte)len);
229 public static void WriteLongstr(NetworkBinaryWriter writer, byte[] val)
231 WriteLong(writer, (uint)val.Length);
235 public static void WriteShort(NetworkBinaryWriter writer, ushort val)
237 writer.Write((ushort)val);
240 public static void WriteLong(NetworkBinaryWriter writer, uint val)
242 writer.Write((uint)val);
245 public static void WriteLonglong(NetworkBinaryWriter writer, ulong val)
247 writer.Write((ulong)val);
250 public static void DecimalToAmqp(decimal value, out byte scale, out int mantissa)
252 // According to the documentation :-
253 // - word 0: low-order "mantissa"
254 // - word 1, word 2: medium- and high-order "mantissa"
255 // - word 3: mostly reserved; "exponent" and sign bit
256 // In one way, this is broader than AMQP: the mantissa is larger.
257 // In another way, smaller: the exponent ranges 0-28 inclusive.
258 // We need to be careful about the range of word 0, too: we can
259 // only take 31 bits worth of it, since the sign bit needs to
261 int[] bitRepresentation = decimal.GetBits(value);
262 if (bitRepresentation[1] != 0 || // mantissa extends into middle word
263 bitRepresentation[2] != 0 || // mantissa extends into top word
264 bitRepresentation[0] < 0) // mantissa extends beyond 31 bits
266 throw new WireFormattingException("Decimal overflow in AMQP encoding", value);
268 scale = (byte)((((uint)bitRepresentation[3]) >> 16) & 0xFF);
269 mantissa = (int)((((uint)bitRepresentation[3]) & 0x80000000) |
270 (((uint)bitRepresentation[0]) & 0x7FFFFFFF));
273 public static void WriteDecimal(NetworkBinaryWriter writer, decimal value)
277 DecimalToAmqp(value, out scale, out mantissa);
278 WriteOctet(writer, scale);
279 WriteLong(writer, (uint)mantissa);
282 ///<summary>Writes an AMQP "table" to the writer.</summary>
285 /// In this method, we assume that the stream that backs our
286 /// NetworkBinaryWriter is a positionable stream - which it is
287 /// currently (see Frame.m_accumulator, Frame.GetWriter and
288 /// Command.Transmit).
291 /// Supports the AMQP 0-8/0-9 standard entry types S, I, D, T
292 /// and F, as well as the QPid-0-8 specific b, d, f, l, s, t
296 public static void WriteTable(NetworkBinaryWriter writer, IDictionary val)
300 writer.Write((uint)0);
304 Stream backingStream = writer.BaseStream;
305 long patchPosition = backingStream.Position;
306 writer.Write((uint)0); // length of table - will be backpatched
308 foreach (DictionaryEntry entry in val)
310 WriteShortstr(writer, (string)entry.Key);
311 object value = entry.Value;
315 WriteOctet(writer, (byte)'V');
317 else if (value is string)
319 WriteOctet(writer, (byte)'S');
320 WriteLongstr(writer, Encoding.UTF8.GetBytes((string)value));
322 else if (value is byte[])
324 WriteOctet(writer, (byte)'S');
325 WriteLongstr(writer, (byte[])value);
327 else if (value is int)
329 WriteOctet(writer, (byte)'I');
330 writer.Write((int)value);
332 else if (value is decimal)
334 WriteOctet(writer, (byte)'D');
335 WriteDecimal(writer, (decimal)value);
337 else if (value is AmqpTimestamp)
339 WriteOctet(writer, (byte)'T');
340 WriteTimestamp(writer, (AmqpTimestamp)value);
342 else if (value is IDictionary)
344 WriteOctet(writer, (byte)'F');
345 WriteTable(writer, (IDictionary)value);
347 else if (value is byte)
349 WriteOctet(writer, (byte)'b');
350 WriteOctet(writer, (byte)value);
352 else if (value is double)
354 WriteOctet(writer, (byte)'d');
355 writer.Write((double)value);
357 else if (value is float)
359 WriteOctet(writer, (byte)'f');
360 writer.Write((float)value);
362 else if (value is long)
364 WriteOctet(writer, (byte)'l');
365 writer.Write((long)value);
367 else if (value is short)
369 WriteOctet(writer, (byte)'s');
370 writer.Write((short)value);
372 else if (value is bool)
374 WriteOctet(writer, (byte)'t');
375 WriteOctet(writer, (byte)(((bool)value) ? 1 : 0));
377 else if (value is BinaryTableValue)
379 WriteOctet(writer, (byte)'x');
380 WriteLongstr(writer, ((BinaryTableValue)value).Bytes);
384 throw new WireFormattingException("Value cannot appear as table value",
389 // Now, backpatch the table length.
390 long savedPosition = backingStream.Position;
391 long tableLength = savedPosition - patchPosition - 4; // offset for length word
392 backingStream.Seek(patchPosition, SeekOrigin.Begin);
393 writer.Write((uint)tableLength);
394 backingStream.Seek(savedPosition, SeekOrigin.Begin);
398 public static void WriteTimestamp(NetworkBinaryWriter writer, AmqpTimestamp val)
400 // 0-9 is afaict silent on the signedness of the timestamp.
401 // See also MethodArgumentReader.ReadTimestamp and AmqpTimestamp itself
402 WriteLonglong(writer, (ulong)val.UnixTime);