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
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, chunk.offset, 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
63         this.chunk = chunk;
64         memory = chunk.memory;
65         tmpNioBuf = nioBuffer;
66         allocator = chunk.arena.parent;
67         this.cache = cache;
68         this.handle = handle;
69         this.offset = offset;
70         this.length = length;
71         this.maxLength = maxLength;
72     }
73
74     /**
75      * Method must be called before reuse this {@link PooledByteBufAllocator}
76      */

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