1 | // Copyright (c) 2006 Dustin Sallings <dustin@spy.net> |
2 | |
3 | package net.spy.memcached.transcoders; |
4 | |
5 | import java.util.Date; |
6 | |
7 | import net.spy.memcached.CachedData; |
8 | |
9 | /** |
10 | * Transcoder that serializes and compresses objects. |
11 | */ |
12 | public class SerializingTranscoder extends BaseSerializingTranscoder |
13 | implements Transcoder<Object> { |
14 | |
15 | // General flags |
16 | static final int SERIALIZED=1; |
17 | static final int COMPRESSED=2; |
18 | |
19 | // Special flags for specially handled types. |
20 | private static final int SPECIAL_MASK=0xff00; |
21 | static final int SPECIAL_BOOLEAN=(1<<8); |
22 | static final int SPECIAL_INT=(2<<8); |
23 | static final int SPECIAL_LONG=(3<<8); |
24 | static final int SPECIAL_DATE=(4<<8); |
25 | static final int SPECIAL_BYTE=(5<<8); |
26 | static final int SPECIAL_FLOAT=(6<<8); |
27 | static final int SPECIAL_DOUBLE=(7<<8); |
28 | static final int SPECIAL_BYTEARRAY=(8<<8); |
29 | |
30 | private final TranscoderUtils tu=new TranscoderUtils(true); |
31 | |
32 | /** |
33 | * Get a serializing transcoder with the default max data size. |
34 | */ |
35 | public SerializingTranscoder() { |
36 | this(CachedData.MAX_SIZE); |
37 | } |
38 | |
39 | /** |
40 | * Get a serializing transcoder that specifies the max data size. |
41 | */ |
42 | public SerializingTranscoder(int max) { |
43 | super(max); |
44 | } |
45 | |
46 | @Override |
47 | public boolean asyncDecode(CachedData d) { |
48 | if((d.getFlags() & COMPRESSED) != 0 |
49 | || (d.getFlags() & SERIALIZED) != 0 ) { |
50 | return true; |
51 | } |
52 | return super.asyncDecode(d); |
53 | } |
54 | |
55 | /* (non-Javadoc) |
56 | * @see net.spy.memcached.Transcoder#decode(net.spy.memcached.CachedData) |
57 | */ |
58 | public Object decode(CachedData d) { |
59 | byte[] data=d.getData(); |
60 | Object rv=null; |
61 | if((d.getFlags() & COMPRESSED) != 0) { |
62 | data=decompress(d.getData()); |
63 | } |
64 | int flags=d.getFlags() & SPECIAL_MASK; |
65 | if((d.getFlags() & SERIALIZED) != 0 && data != null) { |
66 | rv=deserialize(data); |
67 | } else if(flags != 0 && data != null) { |
68 | switch(flags) { |
69 | case SPECIAL_BOOLEAN: |
70 | rv=Boolean.valueOf(tu.decodeBoolean(data)); |
71 | break; |
72 | case SPECIAL_INT: |
73 | rv=new Integer(tu.decodeInt(data)); |
74 | break; |
75 | case SPECIAL_LONG: |
76 | rv=new Long(tu.decodeLong(data)); |
77 | break; |
78 | case SPECIAL_DATE: |
79 | rv=new Date(tu.decodeLong(data)); |
80 | break; |
81 | case SPECIAL_BYTE: |
82 | rv=new Byte(tu.decodeByte(data)); |
83 | break; |
84 | case SPECIAL_FLOAT: |
85 | rv=new Float(Float.intBitsToFloat(tu.decodeInt(data))); |
86 | break; |
87 | case SPECIAL_DOUBLE: |
88 | rv=new Double(Double.longBitsToDouble(tu.decodeLong(data))); |
89 | break; |
90 | case SPECIAL_BYTEARRAY: |
91 | rv=data; |
92 | break; |
93 | default: |
94 | getLogger().warn("Undecodeable with flags %x", flags); |
95 | } |
96 | } else { |
97 | rv=decodeString(data); |
98 | } |
99 | return rv; |
100 | } |
101 | |
102 | /* (non-Javadoc) |
103 | * @see net.spy.memcached.Transcoder#encode(java.lang.Object) |
104 | */ |
105 | public CachedData encode(Object o) { |
106 | byte[] b=null; |
107 | int flags=0; |
108 | if(o instanceof String) { |
109 | b=encodeString((String)o); |
110 | } else if(o instanceof Long) { |
111 | b=tu.encodeLong((Long)o); |
112 | flags |= SPECIAL_LONG; |
113 | } else if(o instanceof Integer) { |
114 | b=tu.encodeInt((Integer)o); |
115 | flags |= SPECIAL_INT; |
116 | } else if(o instanceof Boolean) { |
117 | b=tu.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.floatToRawIntBits((Float)o)); |
127 | flags |= SPECIAL_FLOAT; |
128 | } else if(o instanceof Double) { |
129 | b=tu.encodeLong(Double.doubleToRawLongBits((Double)o)); |
130 | flags |= SPECIAL_DOUBLE; |
131 | } else if(o instanceof byte[]) { |
132 | b=(byte[])o; |
133 | flags |= SPECIAL_BYTEARRAY; |
134 | } else { |
135 | b=serialize(o); |
136 | flags |= SERIALIZED; |
137 | } |
138 | assert b != null; |
139 | if(b.length > compressionThreshold) { |
140 | byte[] compressed=compress(b); |
141 | if(compressed.length < b.length) { |
142 | getLogger().info("Compressed %s from %d to %d", |
143 | o.getClass().getName(), b.length, compressed.length); |
144 | b=compressed; |
145 | flags |= COMPRESSED; |
146 | } else { |
147 | getLogger().info( |
148 | "Compression increased the size of %s from %d to %d", |
149 | o.getClass().getName(), b.length, compressed.length); |
150 | } |
151 | } |
152 | return new CachedData(flags, b, getMaxSize()); |
153 | } |
154 | |
155 | } |