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 static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
20
21 import io.netty.util.ResourceLeakDetector;
22 import io.netty.util.ResourceLeakTracker;
23 import io.netty.util.internal.PlatformDependent;
24 import io.netty.util.internal.StringUtil;
25
26 /**
27  * Skeletal {@link ByteBufAllocator} implementation to extend.
28  */

29 public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
30     static final int DEFAULT_INITIAL_CAPACITY = 256;
31     static final int DEFAULT_MAX_CAPACITY = Integer.MAX_VALUE;
32     static final int DEFAULT_MAX_COMPONENTS = 16;
33     static final int CALCULATE_THRESHOLD = 1048576 * 4; // 4 MiB page
34
35     static {
36         ResourceLeakDetector.addExclusions(AbstractByteBufAllocator.class"toLeakAwareBuffer");
37     }
38
39     protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) {
40         ResourceLeakTracker<ByteBuf> leak;
41         switch (ResourceLeakDetector.getLevel()) {
42             case SIMPLE:
43                 leak = AbstractByteBuf.leakDetector.track(buf);
44                 if (leak != null) {
45                     buf = new SimpleLeakAwareByteBuf(buf, leak);
46                 }
47                 break;
48             case ADVANCED:
49             case PARANOID:
50                 leak = AbstractByteBuf.leakDetector.track(buf);
51                 if (leak != null) {
52                     buf = new AdvancedLeakAwareByteBuf(buf, leak);
53                 }
54                 break;
55             default:
56                 break;
57         }
58         return buf;
59     }
60
61     protected static CompositeByteBuf toLeakAwareBuffer(CompositeByteBuf buf) {
62         ResourceLeakTracker<ByteBuf> leak;
63         switch (ResourceLeakDetector.getLevel()) {
64             case SIMPLE:
65                 leak = AbstractByteBuf.leakDetector.track(buf);
66                 if (leak != null) {
67                     buf = new SimpleLeakAwareCompositeByteBuf(buf, leak);
68                 }
69                 break;
70             case ADVANCED:
71             case PARANOID:
72                 leak = AbstractByteBuf.leakDetector.track(buf);
73                 if (leak != null) {
74                     buf = new AdvancedLeakAwareCompositeByteBuf(buf, leak);
75                 }
76                 break;
77             default:
78                 break;
79         }
80         return buf;
81     }
82
83     private final boolean directByDefault;
84     private final ByteBuf emptyBuf;
85
86     /**
87      * Instance use heap buffers by default
88      */

89     protected AbstractByteBufAllocator() {
90         this(false);
91     }
92
93     /**
94      * Create new instance
95      *
96      * @param preferDirect {@code trueif {@link #buffer(int)} should try to allocate a direct buffer rather than
97      *                     a heap buffer
98      */

99     protected AbstractByteBufAllocator(boolean preferDirect) {
100         directByDefault = preferDirect && PlatformDependent.hasUnsafe();
101         emptyBuf = new EmptyByteBuf(this);
102     }
103
104     @Override
105     public ByteBuf buffer() {
106         if (directByDefault) {
107             return directBuffer();
108         }
109         return heapBuffer();
110     }
111
112     @Override
113     public ByteBuf buffer(int initialCapacity) {
114         if (directByDefault) {
115             return directBuffer(initialCapacity);
116         }
117         return heapBuffer(initialCapacity);
118     }
119
120     @Override
121     public ByteBuf buffer(int initialCapacity, int maxCapacity) {
122         if (directByDefault) {
123             return directBuffer(initialCapacity, maxCapacity);
124         }
125         return heapBuffer(initialCapacity, maxCapacity);
126     }
127
128     @Override
129     public ByteBuf ioBuffer() {
130         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
131             return directBuffer(DEFAULT_INITIAL_CAPACITY);
132         }
133         return heapBuffer(DEFAULT_INITIAL_CAPACITY);
134     }
135
136     @Override
137     public ByteBuf ioBuffer(int initialCapacity) {
138         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
139             return directBuffer(initialCapacity);
140         }
141         return heapBuffer(initialCapacity);
142     }
143
144     @Override
145     public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {
146         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
147             return directBuffer(initialCapacity, maxCapacity);
148         }
149         return heapBuffer(initialCapacity, maxCapacity);
150     }
151
152     @Override
153     public ByteBuf heapBuffer() {
154         return heapBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
155     }
156
157     @Override
158     public ByteBuf heapBuffer(int initialCapacity) {
159         return heapBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
160     }
161
162     @Override
163     public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
164         if (initialCapacity == 0 && maxCapacity == 0) {
165             return emptyBuf;
166         }
167         validate(initialCapacity, maxCapacity);
168         return newHeapBuffer(initialCapacity, maxCapacity);
169     }
170
171     @Override
172     public ByteBuf directBuffer() {
173         return directBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
174     }
175
176     @Override
177     public ByteBuf directBuffer(int initialCapacity) {
178         return directBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
179     }
180
181     @Override
182     public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
183         if (initialCapacity == 0 && maxCapacity == 0) {
184             return emptyBuf;
185         }
186         validate(initialCapacity, maxCapacity);
187         return newDirectBuffer(initialCapacity, maxCapacity);
188     }
189
190     @Override
191     public CompositeByteBuf compositeBuffer() {
192         if (directByDefault) {
193             return compositeDirectBuffer();
194         }
195         return compositeHeapBuffer();
196     }
197
198     @Override
199     public CompositeByteBuf compositeBuffer(int maxNumComponents) {
200         if (directByDefault) {
201             return compositeDirectBuffer(maxNumComponents);
202         }
203         return compositeHeapBuffer(maxNumComponents);
204     }
205
206     @Override
207     public CompositeByteBuf compositeHeapBuffer() {
208         return compositeHeapBuffer(DEFAULT_MAX_COMPONENTS);
209     }
210
211     @Override
212     public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {
213         return toLeakAwareBuffer(new CompositeByteBuf(thisfalse, maxNumComponents));
214     }
215
216     @Override
217     public CompositeByteBuf compositeDirectBuffer() {
218         return compositeDirectBuffer(DEFAULT_MAX_COMPONENTS);
219     }
220
221     @Override
222     public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
223         return toLeakAwareBuffer(new CompositeByteBuf(thistrue, maxNumComponents));
224     }
225
226     private static void validate(int initialCapacity, int maxCapacity) {
227         checkPositiveOrZero(initialCapacity, "initialCapacity");
228         if (initialCapacity > maxCapacity) {
229             throw new IllegalArgumentException(String.format(
230                     "initialCapacity: %d (expected: not greater than maxCapacity(%d)",
231                     initialCapacity, maxCapacity));
232         }
233     }
234
235     /**
236      * Create a heap {@link ByteBuf} with the given initialCapacity and maxCapacity.
237      */

238     protected abstract ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity);
239
240     /**
241      * Create a direct {@link ByteBuf} with the given initialCapacity and maxCapacity.
242      */

243     protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);
244
245     @Override
246     public String toString() {
247         return StringUtil.simpleClassName(this) + "(directByDefault: " + directByDefault + ')';
248     }
249
250     @Override
251     public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
252         checkPositiveOrZero(minNewCapacity, "minNewCapacity");
253         if (minNewCapacity > maxCapacity) {
254             throw new IllegalArgumentException(String.format(
255                     "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
256                     minNewCapacity, maxCapacity));
257         }
258         final int threshold = CALCULATE_THRESHOLD; // 4 MiB page
259
260         if (minNewCapacity == threshold) {
261             return threshold;
262         }
263
264         // If over threshold, do not double but just increase by threshold.
265         if (minNewCapacity > threshold) {
266             int newCapacity = minNewCapacity / threshold * threshold;
267             if (newCapacity > maxCapacity - threshold) {
268                 newCapacity = maxCapacity;
269             } else {
270                 newCapacity += threshold;
271             }
272             return newCapacity;
273         }
274
275         // Not over threshold. Double up to 4 MiB, starting from 64.
276         int newCapacity = 64;
277         while (newCapacity < minNewCapacity) {
278             newCapacity <<= 1;
279         }
280
281         return Math.min(newCapacity, maxCapacity);
282     }
283 }
284