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