View Javadoc
1   /*
2    * Copyright 2012 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;
17  
18  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
19  
20  import io.netty.buffer.ByteBuf;
21  import io.netty.channel.ChannelHandler.Sharable;
22  import io.netty.channel.ChannelHandlerContext;
23  import io.netty.util.internal.ObjectUtil;
24  
25  import java.nio.ByteOrder;
26  import java.util.List;
27  
28  
29  /**
30   * An encoder that prepends the length of the message.  The length value is
31   * prepended as a binary form.
32   * <p>
33   * For example, <tt>{@link LengthFieldPrepender}(2)</tt> will encode the
34   * following 12-bytes string:
35   * <pre>
36   * +----------------+
37   * | "HELLO, WORLD" |
38   * +----------------+
39   * </pre>
40   * into the following:
41   * <pre>
42   * +--------+----------------+
43   * + 0x000C | "HELLO, WORLD" |
44   * +--------+----------------+
45   * </pre>
46   * If you turned on the {@code lengthIncludesLengthFieldLength} flag in the
47   * constructor, the encoded data would look like the following
48   * (12 (original data) + 2 (prepended data) = 14 (0xE)):
49   * <pre>
50   * +--------+----------------+
51   * + 0x000E | "HELLO, WORLD" |
52   * +--------+----------------+
53   * </pre>
54   */
55  @Sharable
56  public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> {
57  
58      private final ByteOrder byteOrder;
59      private final int lengthFieldLength;
60      private final boolean lengthIncludesLengthFieldLength;
61      private final int lengthAdjustment;
62  
63      /**
64       * Creates a new instance.
65       *
66       * @param lengthFieldLength the length of the prepended length field.
67       *                          Only 1, 2, 3, 4, and 8 are allowed.
68       *
69       * @throws IllegalArgumentException
70       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
71       */
72      public LengthFieldPrepender(int lengthFieldLength) {
73          this(lengthFieldLength, false);
74      }
75  
76      /**
77       * Creates a new instance.
78       *
79       * @param lengthFieldLength the length of the prepended length field.
80       *                          Only 1, 2, 3, 4, and 8 are allowed.
81       * @param lengthIncludesLengthFieldLength
82       *                          if {@code true}, the length of the prepended
83       *                          length field is added to the value of the
84       *                          prepended length field.
85       *
86       * @throws IllegalArgumentException
87       *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
88       */
89      public LengthFieldPrepender(int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {
90          this(lengthFieldLength, 0, lengthIncludesLengthFieldLength);
91      }
92  
93      /**
94       * Creates a new instance.
95       *
96       * @param lengthFieldLength the length of the prepended length field.
97       *                          Only 1, 2, 3, 4, and 8 are allowed.
98       * @param lengthAdjustment  the compensation value to add to the value
99       *                          of the length field
100      *
101      * @throws IllegalArgumentException
102      *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
103      */
104     public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment) {
105         this(lengthFieldLength, lengthAdjustment, false);
106     }
107 
108     /**
109      * Creates a new instance.
110      *
111      * @param lengthFieldLength the length of the prepended length field.
112      *                          Only 1, 2, 3, 4, and 8 are allowed.
113      * @param lengthAdjustment  the compensation value to add to the value
114      *                          of the length field
115      * @param lengthIncludesLengthFieldLength
116      *                          if {@code true}, the length of the prepended
117      *                          length field is added to the value of the
118      *                          prepended length field.
119      *
120      * @throws IllegalArgumentException
121      *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
122      */
123     public LengthFieldPrepender(int lengthFieldLength, int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
124         this(ByteOrder.BIG_ENDIAN, lengthFieldLength, lengthAdjustment, lengthIncludesLengthFieldLength);
125     }
126 
127     /**
128      * Creates a new instance.
129      *
130      * @param byteOrder         the {@link ByteOrder} of the length field
131      * @param lengthFieldLength the length of the prepended length field.
132      *                          Only 1, 2, 3, 4, and 8 are allowed.
133      * @param lengthAdjustment  the compensation value to add to the value
134      *                          of the length field
135      * @param lengthIncludesLengthFieldLength
136      *                          if {@code true}, the length of the prepended
137      *                          length field is added to the value of the
138      *                          prepended length field.
139      *
140      * @throws IllegalArgumentException
141      *         if {@code lengthFieldLength} is not 1, 2, 3, 4, or 8
142      */
143     public LengthFieldPrepender(
144             ByteOrder byteOrder, int lengthFieldLength,
145             int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
146         if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
147             lengthFieldLength != 3 && lengthFieldLength != 4 &&
148             lengthFieldLength != 8) {
149             throw new IllegalArgumentException(
150                     "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
151                     lengthFieldLength);
152         }
153         this.byteOrder = ObjectUtil.checkNotNull(byteOrder, "byteOrder");
154         this.lengthFieldLength = lengthFieldLength;
155         this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;
156         this.lengthAdjustment = lengthAdjustment;
157     }
158 
159     @Override
160     protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
161         int length = msg.readableBytes() + lengthAdjustment;
162         if (lengthIncludesLengthFieldLength) {
163             length += lengthFieldLength;
164         }
165 
166         checkPositiveOrZero(length, "length");
167 
168         switch (lengthFieldLength) {
169         case 1:
170             if (length >= 256) {
171                 throw new IllegalArgumentException(
172                         "length does not fit into a byte: " + length);
173             }
174             out.add(ctx.alloc().buffer(1).order(byteOrder).writeByte((byte) length));
175             break;
176         case 2:
177             if (length >= 65536) {
178                 throw new IllegalArgumentException(
179                         "length does not fit into a short integer: " + length);
180             }
181             out.add(ctx.alloc().buffer(2).order(byteOrder).writeShort((short) length));
182             break;
183         case 3:
184             if (length >= 16777216) {
185                 throw new IllegalArgumentException(
186                         "length does not fit into a medium integer: " + length);
187             }
188             out.add(ctx.alloc().buffer(3).order(byteOrder).writeMedium(length));
189             break;
190         case 4:
191             out.add(ctx.alloc().buffer(4).order(byteOrder).writeInt(length));
192             break;
193         case 8:
194             out.add(ctx.alloc().buffer(8).order(byteOrder).writeLong(length));
195             break;
196         default:
197             throw new Error("should not reach here");
198         }
199         out.add(msg.retain());
200     }
201 }