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