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    *   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.buffer;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.OutputStream;
21  import java.nio.ByteBuffer;
22  import java.nio.ByteOrder;
23  import java.nio.channels.ClosedChannelException;
24  import java.nio.channels.GatheringByteChannel;
25  import java.nio.channels.ScatteringByteChannel;
26  
27  /**
28   * A NIO {@link ByteBuffer} based buffer.  It is recommended to use {@link ChannelBuffers#directBuffer(int)}
29   * and {@link ChannelBuffers#wrappedBuffer(ByteBuffer)} instead of calling the
30   * constructor explicitly.
31   */
32  public class ByteBufferBackedChannelBuffer extends AbstractChannelBuffer {
33  
34      private final ByteBuffer buffer;
35      private final ByteOrder order;
36      private final int capacity;
37  
38      /**
39       * Creates a new buffer which wraps the specified buffer's slice.
40       */
41      public ByteBufferBackedChannelBuffer(ByteBuffer buffer) {
42          if (buffer == null) {
43              throw new NullPointerException("buffer");
44          }
45  
46          order = buffer.order();
47          this.buffer = buffer.slice().order(order);
48          capacity = buffer.remaining();
49          writerIndex(capacity);
50      }
51  
52      private ByteBufferBackedChannelBuffer(ByteBufferBackedChannelBuffer buffer) {
53          this.buffer = buffer.buffer;
54          order = buffer.order;
55          capacity = buffer.capacity;
56          setIndex(buffer.readerIndex(), buffer.writerIndex());
57      }
58  
59      public ChannelBufferFactory factory() {
60          if (buffer.isDirect()) {
61              return DirectChannelBufferFactory.getInstance(order());
62          } else {
63              return HeapChannelBufferFactory.getInstance(order());
64          }
65      }
66  
67      public boolean isDirect() {
68          return buffer.isDirect();
69      }
70  
71      public ByteOrder order() {
72          return order;
73      }
74  
75      public int capacity() {
76          return capacity;
77      }
78  
79      public boolean hasArray() {
80          return buffer.hasArray();
81      }
82  
83      public byte[] array() {
84          return buffer.array();
85      }
86  
87      public int arrayOffset() {
88          return buffer.arrayOffset();
89      }
90  
91      public byte getByte(int index) {
92          return buffer.get(index);
93      }
94  
95      public short getShort(int index) {
96          return buffer.getShort(index);
97      }
98  
99      public int getUnsignedMedium(int index) {
100         return  (getByte(index)     & 0xff) << 16 |
101                 (getByte(index + 1) & 0xff) <<  8 |
102                 getByte(index + 2) & 0xff;
103     }
104 
105     public int getInt(int index) {
106         return buffer.getInt(index);
107     }
108 
109     public long getLong(int index) {
110         return buffer.getLong(index);
111     }
112 
113     public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) {
114         if (dst instanceof ByteBufferBackedChannelBuffer) {
115             ByteBufferBackedChannelBuffer bbdst = (ByteBufferBackedChannelBuffer) dst;
116             ByteBuffer data = bbdst.buffer.duplicate();
117 
118             data.limit(dstIndex + length).position(dstIndex);
119             getBytes(index, data);
120         } else if (buffer.hasArray()) {
121             dst.setBytes(dstIndex, buffer.array(), index + buffer.arrayOffset(), length);
122         } else {
123             dst.setBytes(dstIndex, this, index, length);
124         }
125     }
126 
127     public void getBytes(int index, byte[] dst, int dstIndex, int length) {
128         ByteBuffer data = buffer.duplicate();
129         try {
130             data.limit(index + length).position(index);
131         } catch (IllegalArgumentException e) {
132             throw new IndexOutOfBoundsException("Too many bytes to read - Need "
133                     + (index + length) + ", maximum is " + data.limit());
134         }
135         data.get(dst, dstIndex, length);
136     }
137 
138     public void getBytes(int index, ByteBuffer dst) {
139         ByteBuffer data = buffer.duplicate();
140         int bytesToCopy = Math.min(capacity() - index, dst.remaining());
141         try {
142             data.limit(index + bytesToCopy).position(index);
143         } catch (IllegalArgumentException e) {
144             throw new IndexOutOfBoundsException("Too many bytes to read - Need "
145                     + (index + bytesToCopy) + ", maximum is " + data.limit());
146         }
147         dst.put(data);
148     }
149 
150     public void setByte(int index, int value) {
151         buffer.put(index, (byte) value);
152     }
153 
154     public void setShort(int index, int value) {
155         buffer.putShort(index, (short) value);
156     }
157 
158     public void setMedium(int index, int   value) {
159         setByte(index,     (byte) (value >>> 16));
160         setByte(index + 1, (byte) (value >>>  8));
161         setByte(index + 2, (byte) value);
162     }
163 
164     public void setInt(int index, int   value) {
165         buffer.putInt(index, value);
166     }
167 
168     public void setLong(int index, long  value) {
169         buffer.putLong(index, value);
170     }
171 
172     public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) {
173         if (src instanceof ByteBufferBackedChannelBuffer) {
174             ByteBufferBackedChannelBuffer bbsrc = (ByteBufferBackedChannelBuffer) src;
175             ByteBuffer data = bbsrc.buffer.duplicate();
176 
177             data.limit(srcIndex + length).position(srcIndex);
178             setBytes(index, data);
179         } else if (buffer.hasArray()) {
180             src.getBytes(srcIndex, buffer.array(), index + buffer.arrayOffset(), length);
181         } else {
182             src.getBytes(srcIndex, this, index, length);
183         }
184     }
185 
186     public void setBytes(int index, byte[] src, int srcIndex, int length) {
187         ByteBuffer data = buffer.duplicate();
188         data.limit(index + length).position(index);
189         data.put(src, srcIndex, length);
190     }
191 
192     public void setBytes(int index, ByteBuffer src) {
193         ByteBuffer data = buffer.duplicate();
194         data.limit(index + src.remaining()).position(index);
195         data.put(src);
196     }
197 
198     public void getBytes(int index, OutputStream out, int length) throws IOException {
199         if (length == 0) {
200             return;
201         }
202 
203         if (buffer.hasArray()) {
204             out.write(
205                     buffer.array(),
206                     index + buffer.arrayOffset(),
207                     length);
208         } else {
209             byte[] tmp = new byte[length];
210             ((ByteBuffer) buffer.duplicate().position(index)).get(tmp);
211             out.write(tmp);
212         }
213     }
214 
215     public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
216         if (length == 0) {
217             return 0;
218         }
219 
220         return out.write((ByteBuffer) buffer.duplicate().position(index).limit(index + length));
221     }
222 
223     public int setBytes(int index, InputStream in, int length)
224             throws IOException {
225 
226         int readBytes = 0;
227 
228         if (buffer.hasArray()) {
229             index += buffer.arrayOffset();
230             do {
231                 int localReadBytes = in.read(buffer.array(), index, length);
232                 if (localReadBytes < 0) {
233                     if (readBytes == 0) {
234                         return -1;
235                     } else {
236                         break;
237                     }
238                 }
239                 readBytes += localReadBytes;
240                 index += localReadBytes;
241                 length -= localReadBytes;
242             } while (length > 0);
243         } else {
244             byte[] tmp = new byte[length];
245             int i = 0;
246             do {
247                 int localReadBytes = in.read(tmp, i, tmp.length - i);
248                 if (localReadBytes < 0) {
249                     if (readBytes == 0) {
250                         return -1;
251                     } else {
252                         break;
253                     }
254                 }
255                 readBytes += localReadBytes;
256                 i += readBytes;
257             } while (i < tmp.length);
258             ((ByteBuffer) buffer.duplicate().position(index)).put(tmp);
259         }
260 
261         return readBytes;
262     }
263 
264     public int setBytes(int index, ScatteringByteChannel in, int length)
265             throws IOException {
266 
267         ByteBuffer slice = (ByteBuffer) buffer.duplicate().limit(index + length).position(index);
268         int readBytes = 0;
269 
270         while (readBytes < length) {
271             int localReadBytes;
272             try {
273                 localReadBytes = in.read(slice);
274             } catch (ClosedChannelException e) {
275                 localReadBytes = -1;
276             }
277             if (localReadBytes < 0) {
278                 if (readBytes == 0) {
279                     return -1;
280                 } else {
281                     return readBytes;
282                 }
283             }
284             if (localReadBytes == 0) {
285                 break;
286             }
287             readBytes += localReadBytes;
288         }
289 
290         return readBytes;
291     }
292 
293     public ByteBuffer toByteBuffer(int index, int length) {
294         if (index == 0 && length == capacity()) {
295             return buffer.duplicate().order(order());
296         } else {
297             return ((ByteBuffer) buffer.duplicate().position(
298                     index).limit(index + length)).slice().order(order());
299         }
300     }
301 
302     public ChannelBuffer slice(int index, int length) {
303         if (index == 0 && length == capacity()) {
304             ChannelBuffer slice = duplicate();
305             slice.setIndex(0, length);
306             return slice;
307         } else {
308             if (index >= 0 && length == 0) {
309                 return ChannelBuffers.EMPTY_BUFFER;
310             }
311             return new ByteBufferBackedChannelBuffer(
312                     ((ByteBuffer) buffer.duplicate().position(
313                             index).limit(index + length)).order(order()));
314         }
315     }
316 
317     public ChannelBuffer duplicate() {
318         return new ByteBufferBackedChannelBuffer(this);
319     }
320 
321     public ChannelBuffer copy(int index, int length) {
322         ByteBuffer src;
323         try {
324             src = (ByteBuffer) buffer.duplicate().position(index).limit(index + length);
325         } catch (IllegalArgumentException e) {
326             throw new IndexOutOfBoundsException("Too many bytes to read - Need "
327                     + (index + length));
328         }
329 
330         ByteBuffer dst = buffer.isDirect() ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length);
331         dst.put(src);
332         dst.order(order());
333         dst.clear();
334         return new ByteBufferBackedChannelBuffer(dst);
335     }
336 }