1 /*
2 * Copyright 2009 Red Hat, Inc.
3 *
4 * Red Hat licenses this file to you under the Apache License, version 2.0
5 * (the "License"); you may not use this file except in compliance with the
6 * License. You may obtain a copy of the License at:
7 *
8 * http://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 org.jboss.netty.handler.codec.frame;
17
18 import static org.jboss.netty.buffer.ChannelBuffers.*;
19
20 import java.nio.ByteOrder;
21
22 import org.jboss.netty.buffer.ChannelBuffer;
23 import org.jboss.netty.buffer.ChannelBufferFactory;
24 import org.jboss.netty.channel.Channel;
25 import org.jboss.netty.channel.ChannelHandlerContext;
26 import org.jboss.netty.channel.ChannelHandler.Sharable;
27 import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
28
29 /**
30 * An encoder that prepends the length of the message. The length value is
31 * prepended as a binary form. It is encoded in either big endian or little
32 * endian depending on the default {@link ByteOrder} of the current
33 * {@link ChannelBufferFactory}.
34 * <p>
35 * For example, <tt>{@link LengthFieldPrepender}(2)</tt> will encode the
36 * following 12-bytes string:
37 * <pre>
38 * +----------------+
39 * | "HELLO, WORLD" |
40 * +----------------+
41 * </pre>
42 * into the following:
43 * <pre>
44 * +--------+----------------+
45 * + 0x000C | "HELLO, WORLD" |
46 * +--------+----------------+
47 * </pre>
48 * If you turned on the {@code lengthIncludesLengthFieldLength} flag in the
49 * constructor, the encoded data would look like the following
50 * (12 (original data) + 2 (prepended data) = 14 (0xE)):
51 * <pre>
52 * +--------+----------------+
53 * + 0x000E | "HELLO, WORLD" |
54 * +--------+----------------+
55 * </pre>
56 *
57 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
58 * @author <a href="http://gleamynode.net/">Trustin Lee</a>
59 * @version $Rev: 2121 $, $Date: 2010-02-02 09:38:07 +0900 (Tue, 02 Feb 2010) $
60 */
61 @Sharable
62 public class LengthFieldPrepender extends OneToOneEncoder {
63
64 private final int lengthFieldLength;
65 private final boolean lengthIncludesLengthFieldLength;
66
67 /**
68 * Creates a new instance.
69 *
70 * @param lengthFieldLength the length of the prepended length field.
71 * Only 1, 2, 3, 4, and 8 are allowed.
72 *
73 * @throws IllegalArgumentException
74 * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
75 */
76 public LengthFieldPrepender(int lengthFieldLength) {
77 this(lengthFieldLength, false);
78 }
79
80 /**
81 * Creates a new instance.
82 *
83 * @param lengthFieldLength the length of the prepended length field.
84 * Only 1, 2, 3, 4, and 8 are allowed.
85 * @param lengthIncludesLengthFieldLength
86 * if {@code true}, the length of the prepended
87 * length field is added to the value of the
88 * prepended length field.
89 *
90 * @throws IllegalArgumentException
91 * if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
92 */
93 public LengthFieldPrepender(
94 int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {
95 if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
96 lengthFieldLength != 3 && lengthFieldLength != 4 &&
97 lengthFieldLength != 8) {
98 throw new IllegalArgumentException(
99 "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
100 lengthFieldLength);
101 }
102
103 this.lengthFieldLength = lengthFieldLength;
104 this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
105 }
106
107 @Override
108 protected Object encode(
109 ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
110 if (!(msg instanceof ChannelBuffer)) {
111 return msg;
112 }
113
114 ChannelBuffer body = (ChannelBuffer) msg;
115 ChannelBuffer header = channel.getConfig().getBufferFactory().getBuffer(body.order(), lengthFieldLength);
116
117 int length = lengthIncludesLengthFieldLength?
118 body.readableBytes() + lengthFieldLength : body.readableBytes();
119 switch (lengthFieldLength) {
120 case 1:
121 if (length >= 256) {
122 throw new IllegalArgumentException(
123 "length does not fit into a byte: " + length);
124 }
125 header.writeByte((byte) length);
126 break;
127 case 2:
128 if (length >= 65536) {
129 throw new IllegalArgumentException(
130 "length does not fit into a short integer: " + length);
131 }
132 header.writeShort((short) length);
133 break;
134 case 3:
135 if (length >= 16777216) {
136 throw new IllegalArgumentException(
137 "length does not fit into a medium integer: " + length);
138 }
139 header.writeMedium(length);
140 break;
141 case 4:
142 header.writeInt(length);
143 break;
144 case 8:
145 header.writeLong(length);
146 break;
147 default:
148 throw new Error("should not reach here");
149 }
150 return wrappedBuffer(header, body);
151 }
152 }