1 /*
2 * Copyright 2015 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.handler.codec.protobuf;
17
18 import com.google.protobuf.CodedInputStream;
19 import com.google.protobuf.nano.CodedInputByteBufferNano;
20 import io.netty.buffer.ByteBuf;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.handler.codec.ByteToMessageDecoder;
23 import io.netty.handler.codec.CorruptedFrameException;
24
25 import java.util.List;
26
27 /**
28 * A decoder that splits the received {@link ByteBuf}s dynamically by the
29 * value of the Google Protocol Buffers
30 * <a href="https://developers.google.com/protocol-buffers/docs/encoding#varints">Base
31 * 128 Varints</a> integer length field in the message. For example:
32 * <pre>
33 * BEFORE DECODE (302 bytes) AFTER DECODE (300 bytes)
34 * +--------+---------------+ +---------------+
35 * | Length | Protobuf Data |----->| Protobuf Data |
36 * | 0xAC02 | (300 bytes) | | (300 bytes) |
37 * +--------+---------------+ +---------------+
38 * </pre>
39 *
40 * @see CodedInputStream
41 * @see CodedInputByteBufferNano
42 */
43 public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {
44
45 // TODO maxFrameLength + safe skip + fail-fast option
46 // (just like LengthFieldBasedFrameDecoder)
47
48 @Override
49 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
50 throws Exception {
51 in.markReaderIndex();
52 int preIndex = in.readerIndex();
53 int length = readRawVarint32(in);
54 if (preIndex == in.readerIndex()) {
55 return;
56 }
57 if (length < 0) {
58 throw new CorruptedFrameException("negative length: " + length);
59 }
60
61 if (in.readableBytes() < length) {
62 in.resetReaderIndex();
63 } else {
64 out.add(in.readRetainedSlice(length));
65 }
66 }
67
68 /**
69 * Reads variable length 32bit int from buffer
70 *
71 * @return decoded int if buffers readerIndex has been forwarded else nonsense value
72 */
73 private static int readRawVarint32(ByteBuf buffer) {
74 if (!buffer.isReadable()) {
75 return 0;
76 }
77 buffer.markReaderIndex();
78 byte tmp = buffer.readByte();
79 if (tmp >= 0) {
80 return tmp;
81 } else {
82 int result = tmp & 127;
83 if (!buffer.isReadable()) {
84 buffer.resetReaderIndex();
85 return 0;
86 }
87 if ((tmp = buffer.readByte()) >= 0) {
88 result |= tmp << 7;
89 } else {
90 result |= (tmp & 127) << 7;
91 if (!buffer.isReadable()) {
92 buffer.resetReaderIndex();
93 return 0;
94 }
95 if ((tmp = buffer.readByte()) >= 0) {
96 result |= tmp << 14;
97 } else {
98 result |= (tmp & 127) << 14;
99 if (!buffer.isReadable()) {
100 buffer.resetReaderIndex();
101 return 0;
102 }
103 if ((tmp = buffer.readByte()) >= 0) {
104 result |= tmp << 21;
105 } else {
106 result |= (tmp & 127) << 21;
107 if (!buffer.isReadable()) {
108 buffer.resetReaderIndex();
109 return 0;
110 }
111 result |= (tmp = buffer.readByte()) << 28;
112 if (tmp < 0) {
113 throw new CorruptedFrameException("malformed varint.");
114 }
115 }
116 }
117 }
118 return result;
119 }
120 }
121 }