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 io.netty.buffer;
17  
18  import io.netty.util.internal.PlatformDependent;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.nio.ByteBuffer;
24  import java.nio.ByteOrder;
25  import java.nio.channels.ClosedChannelException;
26  import java.nio.channels.GatheringByteChannel;
27  import java.nio.channels.ScatteringByteChannel;
28  
29  /**
30   * A NIO {@link ByteBuffer} based buffer. It is recommended to use
31   * {@link UnpooledByteBufAllocator#directBuffer(int, int)}, {@link Unpooled#directBuffer(int)} and
32   * {@link Unpooled#wrappedBuffer(ByteBuffer)} instead of calling the constructor explicitly.
33   */
34  public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
35  
36      private final ByteBufAllocator alloc;
37  
38      private ByteBuffer buffer;
39      private ByteBuffer tmpNioBuf;
40      private int capacity;
41      private boolean doNotFree;
42  
43      /**
44       * Creates a new direct buffer.
45       *
46       * @param initialCapacity the initial capacity of the underlying direct buffer
47       * @param maxCapacity     the maximum capacity of the underlying direct buffer
48       */
49      public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
50          super(maxCapacity);
51          if (alloc == null) {
52              throw new NullPointerException("alloc");
53          }
54          if (initialCapacity < 0) {
55              throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
56          }
57          if (maxCapacity < 0) {
58              throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
59          }
60          if (initialCapacity > maxCapacity) {
61              throw new IllegalArgumentException(String.format(
62                      "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
63          }
64  
65          this.alloc = alloc;
66          setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
67      }
68  
69      /**
70       * Creates a new direct buffer by wrapping the specified initial buffer.
71       *
72       * @param maxCapacity the maximum capacity of the underlying direct buffer
73       */
74      protected UnpooledDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity) {
75          super(maxCapacity);
76          if (alloc == null) {
77              throw new NullPointerException("alloc");
78          }
79          if (initialBuffer == null) {
80              throw new NullPointerException("initialBuffer");
81          }
82          if (!initialBuffer.isDirect()) {
83              throw new IllegalArgumentException("initialBuffer is not a direct buffer.");
84          }
85          if (initialBuffer.isReadOnly()) {
86              throw new IllegalArgumentException("initialBuffer is a read-only buffer.");
87          }
88  
89          int initialCapacity = initialBuffer.remaining();
90          if (initialCapacity > maxCapacity) {
91              throw new IllegalArgumentException(String.format(
92                      "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
93          }
94  
95          this.alloc = alloc;
96          doNotFree = true;
97          setByteBuffer(initialBuffer.slice().order(ByteOrder.BIG_ENDIAN));
98          writerIndex(initialCapacity);
99      }
100 
101     /**
102      * Allocate a new direct {@link ByteBuffer} with the given initialCapacity.
103      */
104     protected ByteBuffer allocateDirect(int initialCapacity) {
105         return ByteBuffer.allocateDirect(initialCapacity);
106     }
107 
108     /**
109      * Free a direct {@link ByteBuffer}
110      */
111     protected void freeDirect(ByteBuffer buffer) {
112         PlatformDependent.freeDirectBuffer(buffer);
113     }
114 
115     private void setByteBuffer(ByteBuffer buffer) {
116         ByteBuffer oldBuffer = this.buffer;
117         if (oldBuffer != null) {
118             if (doNotFree) {
119                 doNotFree = false;
120             } else {
121                 freeDirect(oldBuffer);
122             }
123         }
124 
125         this.buffer = buffer;
126         tmpNioBuf = null;
127         capacity = buffer.remaining();
128     }
129 
130     @Override
131     public boolean isDirect() {
132         return true;
133     }
134 
135     @Override
136     public int capacity() {
137         return capacity;
138     }
139 
140     @Override
141     public ByteBuf capacity(int newCapacity) {
142         checkNewCapacity(newCapacity);
143 
144         int readerIndex = readerIndex();
145         int writerIndex = writerIndex();
146 
147         int oldCapacity = capacity;
148         if (newCapacity > oldCapacity) {
149             ByteBuffer oldBuffer = buffer;
150             ByteBuffer newBuffer = allocateDirect(newCapacity);
151             oldBuffer.position(0).limit(oldBuffer.capacity());
152             newBuffer.position(0).limit(oldBuffer.capacity());
153             newBuffer.put(oldBuffer);
154             newBuffer.clear();
155             setByteBuffer(newBuffer);
156         } else if (newCapacity < oldCapacity) {
157             ByteBuffer oldBuffer = buffer;
158             ByteBuffer newBuffer = allocateDirect(newCapacity);
159             if (readerIndex < newCapacity) {
160                 if (writerIndex > newCapacity) {
161                     writerIndex(writerIndex = newCapacity);
162                 }
163                 oldBuffer.position(readerIndex).limit(writerIndex);
164                 newBuffer.position(readerIndex).limit(writerIndex);
165                 newBuffer.put(oldBuffer);
166                 newBuffer.clear();
167             } else {
168                 setIndex(newCapacity, newCapacity);
169             }
170             setByteBuffer(newBuffer);
171         }
172         return this;
173     }
174 
175     @Override
176     public ByteBufAllocator alloc() {
177         return alloc;
178     }
179 
180     @Override
181     public ByteOrder order() {
182         return ByteOrder.BIG_ENDIAN;
183     }
184 
185     @Override
186     public boolean hasArray() {
187         return false;
188     }
189 
190     @Override
191     public byte[] array() {
192         throw new UnsupportedOperationException("direct buffer");
193     }
194 
195     @Override
196     public int arrayOffset() {
197         throw new UnsupportedOperationException("direct buffer");
198     }
199 
200     @Override
201     public boolean hasMemoryAddress() {
202         return false;
203     }
204 
205     @Override
206     public long memoryAddress() {
207         throw new UnsupportedOperationException();
208     }
209 
210     @Override
211     public byte getByte(int index) {
212         ensureAccessible();
213         return _getByte(index);
214     }
215 
216     @Override
217     protected byte _getByte(int index) {
218         return buffer.get(index);
219     }
220 
221     @Override
222     public short getShort(int index) {
223         ensureAccessible();
224         return _getShort(index);
225     }
226 
227     @Override
228     protected short _getShort(int index) {
229         return buffer.getShort(index);
230     }
231 
232     @Override
233     public int getUnsignedMedium(int index) {
234         ensureAccessible();
235         return _getUnsignedMedium(index);
236     }
237 
238     @Override
239     protected int _getUnsignedMedium(int index) {
240         return (getByte(index) & 0xff) << 16 | (getByte(index + 1) & 0xff) << 8 | getByte(index + 2) & 0xff;
241     }
242 
243     @Override
244     public int getInt(int index) {
245         ensureAccessible();
246         return _getInt(index);
247     }
248 
249     @Override
250     protected int _getInt(int index) {
251         return buffer.getInt(index);
252     }
253 
254     @Override
255     public long getLong(int index) {
256         ensureAccessible();
257         return _getLong(index);
258     }
259 
260     @Override
261     protected long _getLong(int index) {
262         return buffer.getLong(index);
263     }
264 
265     @Override
266     public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
267         checkDstIndex(index, length, dstIndex, dst.capacity());
268         if (dst.hasArray()) {
269             getBytes(index, dst.array(), dst.arrayOffset() + dstIndex, length);
270         } else if (dst.nioBufferCount() > 0) {
271             for (ByteBuffer bb: dst.nioBuffers(dstIndex, length)) {
272                 int bbLen = bb.remaining();
273                 getBytes(index, bb);
274                 index += bbLen;
275             }
276         } else {
277             dst.setBytes(dstIndex, this, index, length);
278         }
279         return this;
280     }
281 
282     @Override
283     public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
284         getBytes(index, dst, dstIndex, length, false);
285         return this;
286     }
287 
288     private void getBytes(int index, byte[] dst, int dstIndex, int length, boolean internal) {
289         checkDstIndex(index, length, dstIndex, dst.length);
290 
291         ByteBuffer tmpBuf;
292         if (internal) {
293             tmpBuf = internalNioBuffer();
294         } else {
295             tmpBuf = buffer.duplicate();
296         }
297         tmpBuf.clear().position(index).limit(index + length);
298         tmpBuf.get(dst, dstIndex, length);
299     }
300 
301     @Override
302     public ByteBuf readBytes(byte[] dst, int dstIndex, int length) {
303         checkReadableBytes(length);
304         getBytes(readerIndex, dst, dstIndex, length, true);
305         readerIndex += length;
306         return this;
307     }
308 
309     @Override
310     public ByteBuf getBytes(int index, ByteBuffer dst) {
311         getBytes(index, dst, false);
312         return this;
313     }
314 
315     private void getBytes(int index, ByteBuffer dst, boolean internal) {
316         checkIndex(index, dst.remaining());
317 
318         ByteBuffer tmpBuf;
319         if (internal) {
320             tmpBuf = internalNioBuffer();
321         } else {
322             tmpBuf = buffer.duplicate();
323         }
324         tmpBuf.clear().position(index).limit(index + dst.remaining());
325         dst.put(tmpBuf);
326     }
327 
328     @Override
329     public ByteBuf readBytes(ByteBuffer dst) {
330         int length = dst.remaining();
331         checkReadableBytes(length);
332         getBytes(readerIndex, dst, true);
333         readerIndex += length;
334         return this;
335     }
336 
337     @Override
338     public ByteBuf setByte(int index, int value) {
339         ensureAccessible();
340         _setByte(index, value);
341         return this;
342     }
343 
344     @Override
345     protected void _setByte(int index, int value) {
346         buffer.put(index, (byte) value);
347     }
348 
349     @Override
350     public ByteBuf setShort(int index, int value) {
351         ensureAccessible();
352         _setShort(index, value);
353         return this;
354     }
355 
356     @Override
357     protected void _setShort(int index, int value) {
358         buffer.putShort(index, (short) value);
359     }
360 
361     @Override
362     public ByteBuf setMedium(int index, int value) {
363         ensureAccessible();
364         _setMedium(index, value);
365         return this;
366     }
367 
368     @Override
369     protected void _setMedium(int index, int value) {
370         setByte(index, (byte) (value >>> 16));
371         setByte(index + 1, (byte) (value >>> 8));
372         setByte(index + 2, (byte) value);
373     }
374 
375     @Override
376     public ByteBuf setInt(int index, int value) {
377         ensureAccessible();
378         _setInt(index, value);
379         return this;
380     }
381 
382     @Override
383     protected void _setInt(int index, int value) {
384         buffer.putInt(index, value);
385     }
386 
387     @Override
388     public ByteBuf setLong(int index, long value) {
389         ensureAccessible();
390         _setLong(index, value);
391         return this;
392     }
393 
394     @Override
395     protected void _setLong(int index, long value) {
396         buffer.putLong(index, value);
397     }
398 
399     @Override
400     public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
401         checkSrcIndex(index, length, srcIndex, src.capacity());
402         if (src.nioBufferCount() > 0) {
403             for (ByteBuffer bb: src.nioBuffers(srcIndex, length)) {
404                 int bbLen = bb.remaining();
405                 setBytes(index, bb);
406                 index += bbLen;
407             }
408         } else {
409             src.getBytes(srcIndex, this, index, length);
410         }
411         return this;
412     }
413 
414     @Override
415     public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
416         checkSrcIndex(index, length, srcIndex, src.length);
417         ByteBuffer tmpBuf = internalNioBuffer();
418         tmpBuf.clear().position(index).limit(index + length);
419         tmpBuf.put(src, srcIndex, length);
420         return this;
421     }
422 
423     @Override
424     public ByteBuf setBytes(int index, ByteBuffer src) {
425         ensureAccessible();
426         ByteBuffer tmpBuf = internalNioBuffer();
427         if (src == tmpBuf) {
428             src = src.duplicate();
429         }
430 
431         tmpBuf.clear().position(index).limit(index + src.remaining());
432         tmpBuf.put(src);
433         return this;
434     }
435 
436     @Override
437     public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
438         getBytes(index, out, length, false);
439         return this;
440     }
441 
442     private void getBytes(int index, OutputStream out, int length, boolean internal) throws IOException {
443         ensureAccessible();
444         if (length == 0) {
445             return;
446         }
447 
448         if (buffer.hasArray()) {
449             out.write(buffer.array(), index + buffer.arrayOffset(), length);
450         } else {
451             byte[] tmp = new byte[length];
452             ByteBuffer tmpBuf;
453             if (internal) {
454                 tmpBuf = internalNioBuffer();
455             } else {
456                 tmpBuf = buffer.duplicate();
457             }
458             tmpBuf.clear().position(index);
459             tmpBuf.get(tmp);
460             out.write(tmp);
461         }
462     }
463 
464     @Override
465     public ByteBuf readBytes(OutputStream out, int length) throws IOException {
466         checkReadableBytes(length);
467         getBytes(readerIndex, out, length, true);
468         readerIndex += length;
469         return this;
470     }
471 
472     @Override
473     public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
474         return getBytes(index, out, length, false);
475     }
476 
477     private int getBytes(int index, GatheringByteChannel out, int length, boolean internal) throws IOException {
478         ensureAccessible();
479         if (length == 0) {
480             return 0;
481         }
482 
483         ByteBuffer tmpBuf;
484         if (internal) {
485             tmpBuf = internalNioBuffer();
486         } else {
487             tmpBuf = buffer.duplicate();
488         }
489         tmpBuf.clear().position(index).limit(index + length);
490         return out.write(tmpBuf);
491     }
492 
493     @Override
494     public int readBytes(GatheringByteChannel out, int length) throws IOException {
495         checkReadableBytes(length);
496         int readBytes = getBytes(readerIndex, out, length, true);
497         readerIndex += readBytes;
498         return readBytes;
499     }
500 
501     @Override
502     public int setBytes(int index, InputStream in, int length) throws IOException {
503         ensureAccessible();
504         if (buffer.hasArray()) {
505             return in.read(buffer.array(), buffer.arrayOffset() + index, length);
506         } else {
507             byte[] tmp = new byte[length];
508             int readBytes = in.read(tmp);
509             if (readBytes <= 0) {
510                 return readBytes;
511             }
512             ByteBuffer tmpBuf = internalNioBuffer();
513             tmpBuf.clear().position(index);
514             tmpBuf.put(tmp, 0, readBytes);
515             return readBytes;
516         }
517     }
518 
519     @Override
520     public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
521         ensureAccessible();
522         ByteBuffer tmpBuf = internalNioBuffer();
523         tmpBuf.clear().position(index).limit(index + length);
524         try {
525             return in.read(tmpNioBuf);
526         } catch (ClosedChannelException ignored) {
527             return -1;
528         }
529     }
530 
531     @Override
532     public int nioBufferCount() {
533         return 1;
534     }
535 
536     @Override
537     public ByteBuffer[] nioBuffers(int index, int length) {
538         return new ByteBuffer[] { nioBuffer(index, length) };
539     }
540 
541     @Override
542     public ByteBuf copy(int index, int length) {
543         ensureAccessible();
544         ByteBuffer src;
545         try {
546             src = (ByteBuffer) buffer.duplicate().clear().position(index).limit(index + length);
547         } catch (IllegalArgumentException ignored) {
548             throw new IndexOutOfBoundsException("Too many bytes to read - Need " + (index + length));
549         }
550 
551         return alloc().directBuffer(length, maxCapacity()).writeBytes(src);
552     }
553 
554     @Override
555     public ByteBuffer internalNioBuffer(int index, int length) {
556         checkIndex(index, length);
557         return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
558     }
559 
560     private ByteBuffer internalNioBuffer() {
561         ByteBuffer tmpNioBuf = this.tmpNioBuf;
562         if (tmpNioBuf == null) {
563             this.tmpNioBuf = tmpNioBuf = buffer.duplicate();
564         }
565         return tmpNioBuf;
566     }
567 
568     @Override
569     public ByteBuffer nioBuffer(int index, int length) {
570         checkIndex(index, length);
571         return ((ByteBuffer) buffer.duplicate().position(index).limit(index + length)).slice();
572     }
573 
574     @Override
575     protected void deallocate() {
576         ByteBuffer buffer = this.buffer;
577         if (buffer == null) {
578             return;
579         }
580 
581         this.buffer = null;
582 
583         if (!doNotFree) {
584             freeDirect(buffer);
585         }
586     }
587 
588     @Override
589     public ByteBuf unwrap() {
590         return null;
591     }
592 }