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