View Javadoc

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