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  
17  package io.netty.buffer;
18  
19  import io.netty.util.Recycler.EnhancedHandle;
20  import io.netty.util.internal.ObjectPool.Handle;
21  
22  import java.io.IOException;
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  abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
31  
32      private final EnhancedHandle<PooledByteBuf<T>> recyclerHandle;
33  
34      protected PoolChunk<T> chunk;
35      protected long handle;
36      protected T memory;
37      protected int offset;
38      protected int length;
39      int maxLength;
40      PoolThreadCache cache;
41      ByteBuffer tmpNioBuf;
42      private ByteBufAllocator allocator;
43  
44      @SuppressWarnings("unchecked")
45      protected PooledByteBuf(Handle<? extends PooledByteBuf<T>> recyclerHandle, int maxCapacity) {
46          super(maxCapacity);
47          this.recyclerHandle = (EnhancedHandle<PooledByteBuf<T>>) recyclerHandle;
48      }
49  
50      void init(PoolChunk<T> chunk, ByteBuffer nioBuffer,
51                long handle, int offset, int length, int maxLength, PoolThreadCache cache, boolean threadLocal) {
52          init0(chunk, nioBuffer, handle, offset, length, maxLength, cache, true, threadLocal);
53      }
54  
55      void initUnpooled(PoolChunk<T> chunk, int length) {
56          init0(chunk, null, 0, 0, length, length, null, false,
57                  false /* unpooled buffers are never allocated out of the thread-local cache */);
58      }
59  
60      private void init0(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int offset, int length, int maxLength,
61                         PoolThreadCache cache, boolean pooled, boolean threadLocal) {
62          assert handle >= 0;
63          assert chunk != null;
64          assert !PoolChunk.isSubpage(handle) ||
65                  chunk.arena.sizeClass.size2SizeIdx(maxLength) <= chunk.arena.sizeClass.smallMaxSizeIdx:
66                  "Allocated small sub-page handle for a buffer size that isn't \"small.\"";
67  
68          chunk.incrementPinnedMemory(maxLength);
69          this.chunk = chunk;
70          memory = chunk.memory;
71          tmpNioBuf = nioBuffer;
72          allocator = chunk.arena.parent;
73          this.cache = cache;
74          this.handle = handle;
75          this.offset = offset;
76          this.length = length;
77          this.maxLength = maxLength;
78          PooledByteBufAllocator.onAllocateBuffer(this, pooled, threadLocal);
79      }
80  
81      /**
82       * Method must be called before reuse this {@link PooledByteBufAllocator}
83       */
84      final void reuse(int maxCapacity) {
85          maxCapacity(maxCapacity);
86          resetRefCnt();
87          setIndex0(0, 0);
88          discardMarks();
89      }
90  
91      @Override
92      public final int capacity() {
93          return length;
94      }
95  
96      @Override
97      public int maxFastWritableBytes() {
98          return Math.min(maxLength, maxCapacity()) - writerIndex;
99      }
100 
101     @Override
102     public final ByteBuf capacity(int newCapacity) {
103         if (newCapacity == length) {
104             ensureAccessible();
105             return this;
106         }
107         checkNewCapacity(newCapacity);
108         if (!chunk.unpooled) {
109             // If the request capacity does not require reallocation, just update the length of the memory.
110             if (newCapacity > length) {
111                 if (newCapacity <= maxLength) {
112                     length = newCapacity;
113                     return this;
114                 }
115             } else if (newCapacity > maxLength >>> 1 &&
116                     (maxLength > 512 || newCapacity > maxLength - 16)) {
117                 // here newCapacity < length
118                 length = newCapacity;
119                 trimIndicesToCapacity(newCapacity);
120                 return this;
121             }
122         }
123 
124         // Reallocation required.
125         PooledByteBufAllocator.onReallocateBuffer(this, newCapacity);
126         chunk.arena.reallocate(this, newCapacity);
127         return this;
128     }
129 
130     @Override
131     public final ByteBufAllocator alloc() {
132         return allocator;
133     }
134 
135     @Override
136     public final ByteOrder order() {
137         return ByteOrder.BIG_ENDIAN;
138     }
139 
140     @Override
141     public final ByteBuf unwrap() {
142         return null;
143     }
144 
145     @Override
146     public final ByteBuf retainedDuplicate() {
147         return PooledDuplicatedByteBuf.newInstance(this, this, readerIndex(), writerIndex());
148     }
149 
150     @Override
151     public final ByteBuf retainedSlice() {
152         final int index = readerIndex();
153         return retainedSlice(index, writerIndex() - index);
154     }
155 
156     @Override
157     public final ByteBuf retainedSlice(int index, int length) {
158         return PooledSlicedByteBuf.newInstance(this, this, index, length);
159     }
160 
161     protected final ByteBuffer internalNioBuffer() {
162         ByteBuffer tmpNioBuf = this.tmpNioBuf;
163         if (tmpNioBuf == null) {
164             this.tmpNioBuf = tmpNioBuf = newInternalNioBuffer(memory);
165         } else {
166             tmpNioBuf.clear();
167         }
168         return tmpNioBuf;
169     }
170 
171     protected abstract ByteBuffer newInternalNioBuffer(T memory);
172 
173     @Override
174     protected final void deallocate() {
175         if (handle >= 0) {
176             PooledByteBufAllocator.onDeallocateBuffer(this);
177             final long handle = this.handle;
178             this.handle = -1;
179             memory = null;
180             chunk.arena.free(chunk, tmpNioBuf, handle, maxLength, cache);
181             tmpNioBuf = null;
182             chunk = null;
183             cache = null;
184             this.recyclerHandle.unguardedRecycle(this);
185         }
186     }
187 
188     protected final int idx(int index) {
189         return offset + index;
190     }
191 
192     final ByteBuffer _internalNioBuffer(int index, int length, boolean duplicate) {
193         index = idx(index);
194         ByteBuffer buffer = duplicate ? newInternalNioBuffer(memory) : internalNioBuffer();
195         buffer.limit(index + length).position(index);
196         return buffer;
197     }
198 
199     ByteBuffer duplicateInternalNioBuffer(int index, int length) {
200         checkIndex(index, length);
201         return _internalNioBuffer(index, length, true);
202     }
203 
204     @Override
205     public final ByteBuffer internalNioBuffer(int index, int length) {
206         checkIndex(index, length);
207         return _internalNioBuffer(index, length, false);
208     }
209 
210     @Override
211     public final int nioBufferCount() {
212         return 1;
213     }
214 
215     @Override
216     public final ByteBuffer nioBuffer(int index, int length) {
217         return duplicateInternalNioBuffer(index, length).slice();
218     }
219 
220     @Override
221     public final ByteBuffer[] nioBuffers(int index, int length) {
222         return new ByteBuffer[] { nioBuffer(index, length) };
223     }
224 
225     @Override
226     public final boolean isContiguous() {
227         return true;
228     }
229 
230     @Override
231     public final int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
232         return out.write(duplicateInternalNioBuffer(index, length));
233     }
234 
235     @Override
236     public final int readBytes(GatheringByteChannel out, int length) throws IOException {
237         checkReadableBytes(length);
238         int readBytes = out.write(_internalNioBuffer(readerIndex, length, false));
239         readerIndex += readBytes;
240         return readBytes;
241     }
242 
243     @Override
244     public final int getBytes(int index, FileChannel out, long position, int length) throws IOException {
245         return out.write(duplicateInternalNioBuffer(index, length), position);
246     }
247 
248     @Override
249     public final int readBytes(FileChannel out, long position, int length) throws IOException {
250         checkReadableBytes(length);
251         int readBytes = out.write(_internalNioBuffer(readerIndex, length, false), position);
252         readerIndex += readBytes;
253         return readBytes;
254     }
255 
256     @Override
257     public final int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
258         try {
259             return in.read(internalNioBuffer(index, length));
260         } catch (ClosedChannelException ignored) {
261             return -1;
262         }
263     }
264 
265     @Override
266     public final int setBytes(int index, FileChannel in, long position, int length) throws IOException {
267         try {
268             return in.read(internalNioBuffer(index, length), position);
269         } catch (ClosedChannelException ignored) {
270             return -1;
271         }
272     }
273 }