1 | package net.spy.memcached.transcoders; |
2 | |
3 | import java.util.Date; |
4 | |
5 | import net.spy.memcached.CachedData; |
6 | |
7 | /** |
8 | * Transcoder that provides compatibility with Greg Whalin's memcached client. |
9 | */ |
10 | public class WhalinTranscoder extends BaseSerializingTranscoder |
11 | implements Transcoder<Object> { |
12 | |
13 | static final int SPECIAL_BYTE = 1; |
14 | static final int SPECIAL_BOOLEAN = 8192; |
15 | static final int SPECIAL_INT = 4; |
16 | static final int SPECIAL_LONG = 16384; |
17 | static final int SPECIAL_CHARACTER = 16; |
18 | static final int SPECIAL_STRING = 32; |
19 | static final int SPECIAL_STRINGBUFFER = 64; |
20 | static final int SPECIAL_FLOAT = 128; |
21 | static final int SPECIAL_SHORT = 256; |
22 | static final int SPECIAL_DOUBLE = 512; |
23 | static final int SPECIAL_DATE = 1024; |
24 | static final int SPECIAL_STRINGBUILDER = 2048; |
25 | static final int SPECIAL_BYTEARRAY = 4096; |
26 | static final int COMPRESSED = 2; |
27 | static final int SERIALIZED = 8; |
28 | |
29 | private final TranscoderUtils tu=new TranscoderUtils(false); |
30 | |
31 | public WhalinTranscoder() { |
32 | super(CachedData.MAX_SIZE); |
33 | } |
34 | |
35 | /* (non-Javadoc) |
36 | * @see net.spy.memcached.Transcoder#decode(net.spy.memcached.CachedData) |
37 | */ |
38 | public Object decode(CachedData d) { |
39 | byte[] data=d.getData(); |
40 | Object rv=null; |
41 | if((d.getFlags() & COMPRESSED) != 0) { |
42 | data=decompress(d.getData()); |
43 | } |
44 | if((d.getFlags() & SERIALIZED) != 0) { |
45 | rv=deserialize(data); |
46 | } else { |
47 | int f=d.getFlags() & ~COMPRESSED; |
48 | switch(f) { |
49 | case SPECIAL_BOOLEAN: |
50 | rv=Boolean.valueOf(this.decodeBoolean(data)); |
51 | break; |
52 | case SPECIAL_INT: |
53 | rv=new Integer(tu.decodeInt(data)); |
54 | break; |
55 | case SPECIAL_SHORT: |
56 | rv=new Short((short)tu.decodeInt(data)); |
57 | break; |
58 | case SPECIAL_LONG: |
59 | rv=new Long(tu.decodeLong(data)); |
60 | break; |
61 | case SPECIAL_DATE: |
62 | rv=new Date(tu.decodeLong(data)); |
63 | break; |
64 | case SPECIAL_BYTE: |
65 | rv=new Byte(tu.decodeByte(data)); |
66 | break; |
67 | case SPECIAL_FLOAT: |
68 | rv=new Float(Float.intBitsToFloat(tu.decodeInt(data))); |
69 | break; |
70 | case SPECIAL_DOUBLE: |
71 | rv=new Double(Double.longBitsToDouble(tu.decodeLong(data))); |
72 | break; |
73 | case SPECIAL_BYTEARRAY: |
74 | rv=data; |
75 | break; |
76 | case SPECIAL_STRING: |
77 | rv = decodeString(data); |
78 | break; |
79 | case SPECIAL_STRINGBUFFER: |
80 | rv=new StringBuffer(decodeString(data)); |
81 | break; |
82 | case SPECIAL_STRINGBUILDER: |
83 | rv=new StringBuilder(decodeString(data)); |
84 | break; |
85 | case SPECIAL_CHARACTER: |
86 | rv = decodeCharacter(data); |
87 | break; |
88 | default: |
89 | getLogger().warn("Cannot handle data with flags %x", f); |
90 | } |
91 | } |
92 | return rv; |
93 | } |
94 | |
95 | public CachedData encode(Object o) { |
96 | byte[] b=null; |
97 | int flags=0; |
98 | if(o instanceof String) { |
99 | b=encodeString((String)o); |
100 | flags |= SPECIAL_STRING; |
101 | } else if(o instanceof StringBuffer) { |
102 | flags |= SPECIAL_STRINGBUFFER; |
103 | b=encodeString(String.valueOf(o)); |
104 | } else if(o instanceof StringBuilder) { |
105 | flags |= SPECIAL_STRINGBUILDER; |
106 | b=encodeString(String.valueOf(o)); |
107 | } else if(o instanceof Long) { |
108 | b=tu.encodeLong((Long)o); |
109 | flags |= SPECIAL_LONG; |
110 | } else if(o instanceof Integer) { |
111 | b=tu.encodeInt((Integer)o); |
112 | flags |= SPECIAL_INT; |
113 | } else if(o instanceof Short) { |
114 | b=tu.encodeInt((Short)o); |
115 | flags |= SPECIAL_SHORT; |
116 | } else if(o instanceof Boolean) { |
117 | b=this.encodeBoolean((Boolean)o); |
118 | flags |= SPECIAL_BOOLEAN; |
119 | } else if(o instanceof Date) { |
120 | b=tu.encodeLong(((Date)o).getTime()); |
121 | flags |= SPECIAL_DATE; |
122 | } else if(o instanceof Byte) { |
123 | b=tu.encodeByte((Byte)o); |
124 | flags |= SPECIAL_BYTE; |
125 | } else if(o instanceof Float) { |
126 | b=tu.encodeInt(Float.floatToIntBits((Float)o)); |
127 | flags |= SPECIAL_FLOAT; |
128 | } else if(o instanceof Double) { |
129 | b=tu.encodeLong(Double.doubleToLongBits((Double)o)); |
130 | flags |= SPECIAL_DOUBLE; |
131 | } else if(o instanceof byte[]) { |
132 | b=(byte[])o; |
133 | flags |= SPECIAL_BYTEARRAY; |
134 | } else if (o instanceof Character) { |
135 | b = tu.encodeInt((Character) o); |
136 | flags |= SPECIAL_CHARACTER; |
137 | } else { |
138 | b=serialize(o); |
139 | flags |= SERIALIZED; |
140 | } |
141 | assert b != null; |
142 | if(b.length > compressionThreshold) { |
143 | byte[] compressed=compress(b); |
144 | if(compressed.length < b.length) { |
145 | getLogger().info("Compressed %s from %d to %d", |
146 | o.getClass().getName(), b.length, compressed.length); |
147 | b=compressed; |
148 | flags |= COMPRESSED; |
149 | } else { |
150 | getLogger().info( |
151 | "Compression increased the size of %s from %d to %d", |
152 | o.getClass().getName(), b.length, compressed.length); |
153 | } |
154 | } |
155 | return new CachedData(flags, b, getMaxSize()); |
156 | } |
157 | |
158 | protected Character decodeCharacter(byte[] b){ |
159 | return Character.valueOf((char)tu.decodeInt(b)); |
160 | } |
161 | |
162 | public byte[] encodeBoolean(boolean b){ |
163 | byte[] rv = new byte[1]; |
164 | rv[0] = (byte) (b ? 1 : 0); |
165 | return rv; |
166 | } |
167 | |
168 | public boolean decodeBoolean(byte[] in) { |
169 | assert in.length == 1 : "Wrong length for a boolean"; |
170 | return in[0] == 1; |
171 | } |
172 | |
173 | |
174 | } |