blob: eb8bdae80ac48fdf3002fddc6f50167c5132e0e3 [file] [log] [blame]
/*
*
* 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
{
/// <summary>
/// AbstractEncoder
/// </summary>
public abstract class AbstractEncoder : IEncoder
{
private static readonly Dictionary<Type, Code> ENCODINGS = new Dictionary<Type, Code>();
private readonly Dictionary<String, byte[]> str8cache = new Dictionary<String, byte[]>();
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<String, Object>), Code.MAP);
ENCODINGS.Add(typeof (List<Object>), 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<String, Object> map)
{
int pos = BeginSize32();
if (map != null)
{
WriteUint32(map.Count);
WriteMapEntries(map);
}
EndSize32(pos);
}
protected void WriteMapEntries(Dictionary<String, Object> map)
{
foreach (KeyValuePair<String, Object> 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<Object> list)
{
int pos = BeginSize32();
if (list != null)
{
WriteUint32(list.Count);
WriteListEntries(list);
}
EndSize32(pos);
}
protected void WriteListEntries(List<Object> list)
{
foreach (Object value in list)
{
Code type = Encoding(value);
Put((byte) type);
Write(type, value);
}
}
public void WriteArray(List<Object> array)
{
int pos = BeginSize32();
if (array != null)
{
WriteArrayEntries(array);
}
EndSize32(pos);
}
protected void WriteArrayEntries(List<Object> 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<String, Object>) value);
break;
case Code.LIST:
WriteList((List<Object>) value);
break;
case Code.ARRAY:
WriteList((List<Object>) 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;
}
}
}
}