/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using org.apache.qpid.transport.util; namespace org.apache.qpid.transport.codec { /// /// AbstractEncoder /// public abstract class AbstractEncoder : IEncoder { private static readonly Dictionary ENCODINGS = new Dictionary(); private readonly Dictionary str8cache = new Dictionary(); static AbstractEncoder() { ENCODINGS.Add(typeof (Boolean), Code.BOOLEAN); ENCODINGS.Add(typeof (String), Code.STR16); ENCODINGS.Add(typeof (long), Code.INT64); ENCODINGS.Add(typeof (int), Code.INT32); ENCODINGS.Add(typeof (short), Code.INT16); ENCODINGS.Add(typeof (Byte), Code.INT8); ENCODINGS.Add(typeof (Dictionary), Code.MAP); ENCODINGS.Add(typeof (List), Code.LIST); ENCODINGS.Add(typeof (float), Code.FLOAT); ENCODINGS.Add(typeof (Double), Code.DOUBLE); ENCODINGS.Add(typeof (char), Code.CHAR); ENCODINGS.Add(typeof (byte[]), Code.VBIN32); ENCODINGS.Add(typeof (UUID), Code.UUID); } protected abstract void DoPut(byte b); protected abstract void DoPut(MemoryStream src); protected void Put(byte b) { DoPut(b); } protected void Put(MemoryStream src) { DoPut(src); } protected virtual void Put(byte[] bytes) { Put(new MemoryStream(bytes)); } protected abstract int BeginSize8(); protected abstract void EndSize8(int pos); protected abstract int BeginSize16(); protected abstract void EndSize16(int pos); protected abstract int BeginSize32(); protected abstract void EndSize32(int pos); public virtual void WriteUint8(short b) { Debug.Assert(b < 0x100); Put((byte) b); } public virtual void WriteUint16(int s) { Debug.Assert(s < 0x10000); Put((byte) Functions.Lsb(s >> 8)); Put((byte) Functions.Lsb(s)); } public virtual void WriteUint32(long i) { Debug.Assert(i < 0x100000000L); Put((byte) Functions.Lsb(i >> 24)); Put((byte) Functions.Lsb(i >> 16)); Put((byte) Functions.Lsb(i >> 8)); Put((byte) Functions.Lsb(i)); } public void WriteSequenceNo(int i) { WriteUint32(i); } public virtual void WriteUint64(long l) { for (int i = 0; i < 8; i++) { Put((byte) Functions.Lsb(l >> (56 - i*8))); } } public abstract void WriteInt8(short b) ; public abstract void WriteInt16(int s) ; public abstract void WriteInt32(long i) ; public abstract void WriteInt64(long l) ; public abstract void WriteFloat(float f) ; public abstract void WriteDouble(double d) ; public void WriteDatetime(long l) { WriteUint64(l); } private static byte[] Encode(String s, Encoding encoding) { return encoding.GetBytes(s); } public void WriteStr8(String s) { if (s == null) { s = ""; } byte[] bytes; if (! str8cache.ContainsKey(s)) { bytes = Encode(s, System.Text.Encoding.UTF8); str8cache.Add(s, bytes); } else { bytes = str8cache[s]; } WriteUint8((short) bytes.Length); Put(bytes); } public void WriteStr16(String s) { if (s == null) { s = ""; } byte[] bytes = Encode(s, System.Text.Encoding.UTF8); WriteUint16(bytes.Length); Put(bytes); } public void WriteVbin8(byte[] bytes) { if (bytes == null) { bytes = new byte[0]; } if (bytes.Length > 255) { throw new Exception("array too long: " + bytes.Length); } WriteUint8((short) bytes.Length); Put(bytes); } public void WriteVbin16(byte[] bytes) { if (bytes == null) { bytes = new byte[0]; } WriteUint16(bytes.Length); Put(bytes); } public void WriteVbin32(byte[] bytes) { if (bytes == null) { bytes = new byte[0]; } WriteUint32(bytes.Length); Put(bytes); } public void WriteSequenceSet(RangeSet ranges) { if (ranges == null) { WriteUint16(0); } else { WriteUint16(ranges.Size()*8); foreach (Range range in ranges) { WriteSequenceNo(range.Lower); WriteSequenceNo(range.Upper); } } } public void WriteByteRanges(RangeSet ranges) { throw new Exception("not implemented"); } public void WriteUuid(UUID uuid) { long msb = 0; long lsb = 0; if (uuid != null) { msb = uuid.MostSignificantBits; lsb = uuid.LeastSignificantBits; } WriteUint64(msb); WriteUint64(lsb); } public void WriteStruct(int type, Struct s) { if (s == null) { s = Struct.Create(type); } int width = s.GetSizeWidth(); int pos = -1; if (width > 0) { pos = BeginSize(width); } if (type > 0) { WriteUint16(type); } s.Write(this); if (width > 0) { EndSize(width, pos); } } public void WriteStruct32(Struct s) { if (s == null) { WriteUint32(0); } else { int pos = BeginSize32(); WriteUint16(s.GetEncodedType()); s.Write(this); EndSize32(pos); } } private Code Encoding(Object value) { if (value == null) { return Code.VOID; } Type klass = value.GetType(); Code type = Resolve(klass); if (type == Code.VOID) { throw new Exception ("unable to resolve type: " + klass + ", " + value); } else { return type; } } private static Code Resolve(Type klass) { Code type; if(ENCODINGS.ContainsKey(klass)) { return ENCODINGS[klass]; } Type sup = klass.BaseType; if (sup != null) { type = Resolve(sup); if (type != Code.VOID) { return type; } } foreach (Type iface in klass.GetInterfaces()) { type = Resolve(iface); if (type != Code.VOID) { return type; } } return Code.VOID; } public void WriteMap(Dictionary map) { int pos = BeginSize32(); if (map != null) { WriteUint32(map.Count); WriteMapEntries(map); } EndSize32(pos); } protected void WriteMapEntries(Dictionary map) { foreach (KeyValuePair entry in map) { String key = entry.Key; Object value = entry.Value; Code type = Encoding(value); WriteStr8(key); Put((byte) type); Write(type, value); } } public void WriteList(List list) { int pos = BeginSize32(); if (list != null) { WriteUint32(list.Count); WriteListEntries(list); } EndSize32(pos); } protected void WriteListEntries(List list) { foreach (Object value in list) { Code type = Encoding(value); Put((byte) type); Write(type, value); } } public void WriteArray(List array) { int pos = BeginSize32(); if (array != null) { WriteArrayEntries(array); } EndSize32(pos); } protected void WriteArrayEntries(List array) { Code type; if (array.Count == 0) { return; } else { type = Encoding(array[0]); } Put((byte) type); WriteUint32(array.Count); foreach (Object value in array) { Write(type, value); } } private void WriteSize(QpidType t, int size) { if (t.Fixed) { if (size != t.width) { throw new Exception("size does not match fixed width " + t.width + ": " + size); } } else { WriteSize(t.width, size); } } private void WriteSize(int width, int size) { // XXX: should check lengths switch (width) { case 1: WriteUint8((short) size); break; case 2: WriteUint16(size); break; case 4: WriteUint32(size); break; default: throw new Exception("illegal width: " + width); } } private int BeginSize(int width) { switch (width) { case 1: return BeginSize8(); case 2: return BeginSize16(); case 4: return BeginSize32(); default: throw new Exception("illegal width: " + width); } } private void EndSize(int width, int pos) { switch (width) { case 1: EndSize8(pos); break; case 2: EndSize16(pos); break; case 4: EndSize32(pos); break; default: throw new Exception("illegal width: " + width); } } private void WriteBytes(QpidType t, byte[] bytes) { WriteSize(t, bytes.Length); Put(bytes); } private void Write(Code t, Object value) { switch (t) { case Code.BIN8: case Code.UINT8: WriteUint8((short) value); break; case Code.INT8: Put((Byte) value); break; case Code.CHAR: byte[] b = BitConverter.GetBytes((char) value); Put(b[0]); break; case Code.BOOLEAN: if ((bool) value) { Put(1); } else { Put(0); } break; case Code.BIN16: case Code.UINT16: WriteUint16((int) value); break; case Code.INT16: WriteUint16((short) value); break; case Code.BIN32: case Code.UINT32: WriteUint32((long) value); break; case Code.CHAR_UTF32: case Code.INT32: WriteUint32((int) value); break; case Code.FLOAT: WriteUint32(BitConverter.DoubleToInt64Bits((float) value) >> 32); break; case Code.BIN64: case Code.UINT64: case Code.INT64: case Code.DATETIME: WriteUint64((long) value); break; case Code.DOUBLE: WriteUint64( BitConverter.DoubleToInt64Bits((double) value)); break; case Code.UUID: WriteUuid((UUID) value); break; case Code.STR8: WriteStr8((string) value); break; case Code.STR16: WriteStr16((string) value); break; case Code.STR8_LATIN: case Code.STR8_UTF16: case Code.STR16_LATIN: case Code.STR16_UTF16: // XXX: need to do character conversion WriteBytes(QpidType.get((byte) t), Encode((string) value, System.Text.Encoding.Unicode)); break; case Code.MAP: WriteMap((Dictionary) value); break; case Code.LIST: WriteList((List) value); break; case Code.ARRAY: WriteList((List) value); break; case Code.STRUCT32: WriteStruct32((Struct) value); break; case Code.BIN40: case Code.DEC32: case Code.BIN72: case Code.DEC64: // XXX: what types are we supposed to use here? WriteBytes(QpidType.get((byte) t), (byte[]) value); break; case Code.VOID: break; default: WriteBytes(QpidType.get((byte) t), (byte[]) value); break; } } } }