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