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