ProtobufDecoder.java revision 420c9859
1package muduo.codec;
2
3import java.nio.charset.Charset;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.zip.Adler32;
7
8import org.jboss.netty.buffer.ChannelBuffer;
9import org.jboss.netty.channel.Channel;
10import org.jboss.netty.channel.ChannelHandler.Sharable;
11import org.jboss.netty.channel.ChannelHandlerContext;
12import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
13
14import com.google.protobuf.Message;
15
16@Sharable
17public class ProtobufDecoder extends OneToOneDecoder {
18
19    private Map<String, Message> knownTypes = new HashMap<String, Message>();
20
21    @Override
22    public Object decode(ChannelHandlerContext ctx, Channel channel, Object obj)
23            throws Exception {
24        if (!(obj instanceof ChannelBuffer)) {
25            return obj;
26        }
27        ChannelBuffer buffer = (ChannelBuffer) obj;
28        if (buffer.readableBytes() >= 10 && checksum(buffer)) {
29            int nameLen = buffer.readInt();
30            String typeName = buffer.toString(buffer.readerIndex(), nameLen - 1,
31                    Charset.defaultCharset());
32            buffer.readerIndex(buffer.readerIndex() + nameLen);
33            Message prototype = knownTypes.get(typeName);
34            if (prototype != null) {
35                return prototype.newBuilderForType().mergeFrom(buffer.array(),
36                        buffer.arrayOffset() + buffer.readerIndex(),
37                        buffer.readableBytes() - 4).build();
38            }
39        }
40        return obj;
41    }
42
43    private boolean checksum(ChannelBuffer buffer) {
44        Adler32 adler32 = new Adler32();
45        adler32.update(buffer.array(),
46                buffer.arrayOffset() + buffer.readerIndex(),
47                buffer.readableBytes() - 4);
48        buffer.markReaderIndex();
49        buffer.readerIndex(buffer.writerIndex() - 4);
50        int checksum = buffer.readInt();
51        buffer.resetReaderIndex();
52        return checksum == (int) adler32.getValue();
53    }
54
55    public void addMessageType(Message message) {
56        knownTypes.put(message.getDescriptorForType().getFullName(), message);
57    }
58}
59