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