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.FileChannel;
27  import java.nio.channels.GatheringByteChannel;
28  import java.nio.channels.ScatteringByteChannel;
29  
30  /**
31   * A NIO {@link ByteBuffer} based buffer. It is recommended to use
32   * {@link UnpooledByteBufAllocator#directBuffer(int, int)}, {@link Unpooled#directBuffer(int)} and
33   * {@link Unpooled#wrappedBuffer(ByteBuffer)} instead of calling the constructor explicitly.}
34   */
35  public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf {
36  
37      private final ByteBufAllocator alloc;
38  
39      private ByteBuffer tmpNioBuf;
40      private int capacity;
41      private boolean doNotFree;
42      ByteBuffer buffer;
43      long memoryAddress;
44  
45      /**
46       * Creates a new direct buffer.
47       *
48       * @param initialCapacity the initial capacity of the underlying direct buffer
49       * @param maxCapacity     the maximum capacity of the underlying direct buffer
50       */
51      public UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
52          super(maxCapacity);
53          if (alloc == null) {
54              throw new NullPointerException("alloc");
55          }
56          if (initialCapacity < 0) {
57              throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
58          }
59          if (maxCapacity < 0) {
60              throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
61          }
62          if (initialCapacity > maxCapacity) {
63              throw new IllegalArgumentException(String.format(
64                      "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
65          }
66  
67          this.alloc = alloc;
68          setByteBuffer(allocateDirect(initialCapacity), false);
69      }
70  
71      /**
72       * Creates a new direct buffer by wrapping the specified initial buffer.
73       *
74       * @param maxCapacity the maximum capacity of the underlying direct buffer
75       */
76      protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity) {
77          // We never try to free the buffer if it was provided by the end-user as we not know if this is an duplicate or
78          // an slice. This is done to prevent an IllegalArgumentException when using Java9 as Unsafe.invokeCleaner(...)
79          // will check if the given buffer is either an duplicate or slice and in this case throw an
80          // IllegalArgumentException.
81          //
82          // See http://hg.openjdk.java.net/jdk9/hs-demo/jdk/file/0d2ab72ba600/src/jdk.unsupported/share/classes/
83          // sun/misc/Unsafe.java#l1250
84          //
85          // We also call slice() explicitly here to preserve behaviour with previous netty releases.
86          this(alloc, initialBuffer.slice(), maxCapacity, false);
87      }
88  
89      UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, ByteBuffer initialBuffer, int maxCapacity, boolean doFree) {
90          super(maxCapacity);
91          if (alloc == null) {
92              throw new NullPointerException("alloc");
93          }
94          if (initialBuffer == null) {
95              throw new NullPointerException("initialBuffer");
96          }
97          if (!initialBuffer.isDirect()) {
98              throw new IllegalArgumentException("initialBuffer is not a direct buffer.");
99          }
100         if (initialBuffer.isReadOnly()) {
101             throw new IllegalArgumentException("initialBuffer is a read-only buffer.");
102         }
103 
104         int initialCapacity = initialBuffer.remaining();
105         if (initialCapacity > maxCapacity) {
106             throw new IllegalArgumentException(String.format(
107                     "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
108         }
109 
110         this.alloc = alloc;
111         doNotFree = !doFree;
112         setByteBuffer(initialBuffer.order(ByteOrder.BIG_ENDIAN), false);
113         writerIndex(initialCapacity);
114     }
115 
116     /**
117      * Allocate a new direct {@link ByteBuffer} with the given initialCapacity.
118      */
119     protected ByteBuffer allocateDirect(int initialCapacity) {
120         return ByteBuffer.allocateDirect(initialCapacity);
121     }
122 
123     /**
124      * Free a direct {@link ByteBuffer}
125      */
126     protected void freeDirect(ByteBuffer buffer) {
127         PlatformDependent.freeDirectBuffer(buffer);
128     }
129 
130     final void setByteBuffer(ByteBuffer buffer, boolean tryFree) {
131         if (tryFree) {
132             ByteBuffer oldBuffer = this.buffer;
133             if (oldBuffer != null) {
134                 if (doNotFree) {
135                     doNotFree = false;
136                 } else {
137                     freeDirect(oldBuffer);
138                 }
139             }
140         }
141         this.buffer = buffer;
142         memoryAddress = PlatformDependent.directBufferAddress(buffer);
143         tmpNioBuf = null;
144         capacity = buffer.remaining();
145     }
146 
147     @Override
148     public boolean isDirect() {
149         return true;
150     }
151 
152     @Override
153     public int capacity() {
154         return capacity;
155     }
156 
157     @Override
158     public ByteBuf capacity(int newCapacity) {
159         checkNewCapacity(newCapacity);
160 
161         int readerIndex = readerIndex();
162         int writerIndex = writerIndex();
163 
164         int oldCapacity = capacity;
165         if (newCapacity > oldCapacity) {
166             ByteBuffer oldBuffer = buffer;
167             ByteBuffer newBuffer = allocateDirect(newCapacity);
168             oldBuffer.position(0).limit(oldBuffer.capacity());
169             newBuffer.position(0).limit(oldBuffer.capacity());
170             newBuffer.put(oldBuffer);
171             newBuffer.clear();
172             setByteBuffer(newBuffer, true);
173         } else if (newCapacity < oldCapacity) {
174             ByteBuffer oldBuffer = buffer;
175             ByteBuffer newBuffer = allocateDirect(newCapacity);
176             if (readerIndex < newCapacity) {
177                 if (writerIndex > newCapacity) {
178                     writerIndex(writerIndex = newCapacity);
179                 }
180                 oldBuffer.position(readerIndex).limit(writerIndex);
181                 newBuffer.position(readerIndex).limit(writerIndex);
182                 newBuffer.put(oldBuffer);
183                 newBuffer.clear();
184             } else {
185                 setIndex(newCapacity, newCapacity);
186             }
187             setByteBuffer(newBuffer, true);
188         }
189         return this;
190     }
191 
192     @Override
193     public ByteBufAllocator alloc() {
194         return alloc;
195     }
196 
197     @Override
198     public ByteOrder order() {
199         return ByteOrder.BIG_ENDIAN;
200     }
201 
202     @Override
203     public boolean hasArray() {
204         return false;
205     }
206 
207     @Override
208     public byte[] array() {
209         throw new UnsupportedOperationException("direct buffer");
210     }
211 
212     @Override
213     public int arrayOffset() {
214         throw new UnsupportedOperationException("direct buffer");
215     }
216 
217     @Override
218     public boolean hasMemoryAddress() {
219         return true;
220     }
221 
222     @Override
223     public long memoryAddress() {
224         ensureAccessible();
225         return memoryAddress;
226     }
227 
228     @Override
229     protected byte _getByte(int index) {
230         return UnsafeByteBufUtil.getByte(addr(index));
231     }
232 
233     @Override
234     protected short _getShort(int index) {
235         return UnsafeByteBufUtil.getShort(addr(index));
236     }
237 
238     @Override
239     protected short _getShortLE(int index) {
240         return UnsafeByteBufUtil.getShortLE(addr(index));
241     }
242 
243     @Override
244     protected int _getUnsignedMedium(int index) {
245         return UnsafeByteBufUtil.getUnsignedMedium(addr(index));
246     }
247 
248     @Override
249     protected int _getUnsignedMediumLE(int index) {
250         return UnsafeByteBufUtil.getUnsignedMediumLE(addr(index));
251     }
252 
253     @Override
254     protected int _getInt(int index) {
255         return UnsafeByteBufUtil.getInt(addr(index));
256     }
257 
258     @Override
259     protected int _getIntLE(int index) {
260         return UnsafeByteBufUtil.getIntLE(addr(index));
261     }
262 
263     @Override
264     protected long _getLong(int index) {
265         return UnsafeByteBufUtil.getLong(addr(index));
266     }
267 
268     @Override
269     protected long _getLongLE(int index) {
270         return UnsafeByteBufUtil.getLongLE(addr(index));
271     }
272 
273     @Override
274     public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
275         UnsafeByteBufUtil.getBytes(this, addr(index), index, dst, dstIndex, length);
276         return this;
277     }
278 
279     @Override
280     public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
281         UnsafeByteBufUtil.getBytes(this, addr(index), index, dst, dstIndex, length);
282         return this;
283     }
284 
285     @Override
286     public ByteBuf getBytes(int index, ByteBuffer dst) {
287         UnsafeByteBufUtil.getBytes(this, addr(index), index, dst);
288         return this;
289     }
290 
291     @Override
292     public ByteBuf readBytes(ByteBuffer dst) {
293         int length = dst.remaining();
294         checkReadableBytes(length);
295         getBytes(readerIndex, dst);
296         readerIndex += length;
297         return this;
298     }
299 
300     @Override
301     protected void _setByte(int index, int value) {
302         UnsafeByteBufUtil.setByte(addr(index), value);
303     }
304 
305     @Override
306     protected void _setShort(int index, int value) {
307         UnsafeByteBufUtil.setShort(addr(index), value);
308     }
309 
310     @Override
311     protected void _setShortLE(int index, int value) {
312         UnsafeByteBufUtil.setShortLE(addr(index), value);
313     }
314 
315     @Override
316     protected void _setMedium(int index, int value) {
317         UnsafeByteBufUtil.setMedium(addr(index), value);
318     }
319 
320     @Override
321     protected void _setMediumLE(int index, int value) {
322         UnsafeByteBufUtil.setMediumLE(addr(index), value);
323     }
324 
325     @Override
326     protected void _setInt(int index, int value) {
327         UnsafeByteBufUtil.setInt(addr(index), value);
328     }
329 
330     @Override
331     protected void _setIntLE(int index, int value) {
332         UnsafeByteBufUtil.setIntLE(addr(index), value);
333     }
334 
335     @Override
336     protected void _setLong(int index, long value) {
337         UnsafeByteBufUtil.setLong(addr(index), value);
338     }
339 
340     @Override
341     protected void _setLongLE(int index, long value) {
342         UnsafeByteBufUtil.setLongLE(addr(index), value);
343     }
344 
345     @Override
346     public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
347         UnsafeByteBufUtil.setBytes(this, addr(index), index, src, srcIndex, length);
348         return this;
349     }
350 
351     @Override
352     public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
353         UnsafeByteBufUtil.setBytes(this, addr(index), index, src, srcIndex, length);
354         return this;
355     }
356 
357     @Override
358     public ByteBuf setBytes(int index, ByteBuffer src) {
359         UnsafeByteBufUtil.setBytes(this, addr(index), index, src);
360         return this;
361     }
362 
363     @Override
364     public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException {
365         UnsafeByteBufUtil.getBytes(this, addr(index), index, out, length);
366         return this;
367     }
368 
369     @Override
370     public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
371         return getBytes(index, out, length, false);
372     }
373 
374     private int getBytes(int index, GatheringByteChannel out, int length, boolean internal) throws IOException {
375         ensureAccessible();
376         if (length == 0) {
377             return 0;
378         }
379 
380         ByteBuffer tmpBuf;
381         if (internal) {
382             tmpBuf = internalNioBuffer();
383         } else {
384             tmpBuf = buffer.duplicate();
385         }
386         tmpBuf.clear().position(index).limit(index + length);
387         return out.write(tmpBuf);
388     }
389 
390     @Override
391     public int getBytes(int index, FileChannel out, long position, int length) throws IOException {
392         return getBytes(index, out, position, length, false);
393     }
394 
395     private int getBytes(int index, FileChannel out, long position, int length, boolean internal) throws IOException {
396         ensureAccessible();
397         if (length == 0) {
398             return 0;
399         }
400 
401         ByteBuffer tmpBuf = internal ? internalNioBuffer() : buffer.duplicate();
402         tmpBuf.clear().position(index).limit(index + length);
403         return out.write(tmpBuf, position);
404     }
405 
406     @Override
407     public int readBytes(GatheringByteChannel out, int length) throws IOException {
408         checkReadableBytes(length);
409         int readBytes = getBytes(readerIndex, out, length, true);
410         readerIndex += readBytes;
411         return readBytes;
412     }
413 
414     @Override
415     public int readBytes(FileChannel out, long position, int length) throws IOException {
416         checkReadableBytes(length);
417         int readBytes = getBytes(readerIndex, out, position, length, true);
418         readerIndex += readBytes;
419         return readBytes;
420     }
421 
422     @Override
423     public int setBytes(int index, InputStream in, int length) throws IOException {
424         return UnsafeByteBufUtil.setBytes(this, addr(index), index, in, length);
425     }
426 
427     @Override
428     public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
429         ensureAccessible();
430         ByteBuffer tmpBuf = internalNioBuffer();
431         tmpBuf.clear().position(index).limit(index + length);
432         try {
433             return in.read(tmpBuf);
434         } catch (ClosedChannelException ignored) {
435             return -1;
436         }
437     }
438 
439     @Override
440     public int setBytes(int index, FileChannel in, long position, int length) throws IOException {
441         ensureAccessible();
442         ByteBuffer tmpBuf = internalNioBuffer();
443         tmpBuf.clear().position(index).limit(index + length);
444         try {
445             return in.read(tmpBuf, position);
446         } catch (ClosedChannelException ignored) {
447             return -1;
448         }
449     }
450 
451     @Override
452     public int nioBufferCount() {
453         return 1;
454     }
455 
456     @Override
457     public ByteBuffer[] nioBuffers(int index, int length) {
458         return new ByteBuffer[] { nioBuffer(index, length) };
459     }
460 
461     @Override
462     public ByteBuf copy(int index, int length) {
463         return UnsafeByteBufUtil.copy(this, addr(index), index, length);
464     }
465 
466     @Override
467     public ByteBuffer internalNioBuffer(int index, int length) {
468         checkIndex(index, length);
469         return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
470     }
471 
472     private ByteBuffer internalNioBuffer() {
473         ByteBuffer tmpNioBuf = this.tmpNioBuf;
474         if (tmpNioBuf == null) {
475             this.tmpNioBuf = tmpNioBuf = buffer.duplicate();
476         }
477         return tmpNioBuf;
478     }
479 
480     @Override
481     public ByteBuffer nioBuffer(int index, int length) {
482         checkIndex(index, length);
483         return ((ByteBuffer) buffer.duplicate().position(index).limit(index + length)).slice();
484     }
485 
486     @Override
487     protected void deallocate() {
488         ByteBuffer buffer = this.buffer;
489         if (buffer == null) {
490             return;
491         }
492 
493         this.buffer = null;
494 
495         if (!doNotFree) {
496             freeDirect(buffer);
497         }
498     }
499 
500     @Override
501     public ByteBuf unwrap() {
502         return null;
503     }
504 
505     long addr(int index) {
506         return memoryAddress + index;
507     }
508 
509     @Override
510     protected SwappedByteBuf newSwappedByteBuf() {
511         if (PlatformDependent.isUnaligned()) {
512             // Only use if unaligned access is supported otherwise there is no gain.
513             return new UnsafeDirectSwappedByteBuf(this);
514         }
515         return super.newSwappedByteBuf();
516     }
517 
518     @Override
519     public ByteBuf setZero(int index, int length) {
520         checkIndex(index, length);
521         UnsafeByteBufUtil.setZero(addr(index), length);
522         return this;
523     }
524 
525     @Override
526     public ByteBuf writeZero(int length) {
527         ensureWritable(length);
528         int wIndex = writerIndex;
529         UnsafeByteBufUtil.setZero(addr(wIndex), length);
530         writerIndex = wIndex + length;
531         return this;
532     }
533 }