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