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 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.MathUtil;
24  import io.netty.util.internal.PlatformDependent;
25  import io.netty.util.internal.StringUtil;
26  
27  /**
28   * Skeletal {@link ByteBufAllocator} implementation to extend.
29   */
30  public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
31      static final int DEFAULT_INITIAL_CAPACITY = 256;
32      static final int DEFAULT_MAX_CAPACITY = Integer.MAX_VALUE;
33      static final int DEFAULT_MAX_COMPONENTS = 16;
34      static final int CALCULATE_THRESHOLD = 1048576 * 4; // 4 MiB page
35  
36      static {
37          ResourceLeakDetector.addExclusions(AbstractByteBufAllocator.class, "toLeakAwareBuffer");
38      }
39  
40      protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) {
41          ResourceLeakTracker<ByteBuf> leak;
42          switch (ResourceLeakDetector.getLevel()) {
43              case SIMPLE:
44                  leak = AbstractByteBuf.leakDetector.track(buf);
45                  if (leak != null) {
46                      buf = new SimpleLeakAwareByteBuf(buf, leak);
47                  }
48                  break;
49              case ADVANCED:
50              case PARANOID:
51                  leak = AbstractByteBuf.leakDetector.track(buf);
52                  if (leak != null) {
53                      buf = new AdvancedLeakAwareByteBuf(buf, leak);
54                  }
55                  break;
56              default:
57                  break;
58          }
59          return buf;
60      }
61  
62      protected static CompositeByteBuf toLeakAwareBuffer(CompositeByteBuf buf) {
63          ResourceLeakTracker<ByteBuf> leak;
64          switch (ResourceLeakDetector.getLevel()) {
65              case SIMPLE:
66                  leak = AbstractByteBuf.leakDetector.track(buf);
67                  if (leak != null) {
68                      buf = new SimpleLeakAwareCompositeByteBuf(buf, leak);
69                  }
70                  break;
71              case ADVANCED:
72              case PARANOID:
73                  leak = AbstractByteBuf.leakDetector.track(buf);
74                  if (leak != null) {
75                      buf = new AdvancedLeakAwareCompositeByteBuf(buf, leak);
76                  }
77                  break;
78              default:
79                  break;
80          }
81          return buf;
82      }
83  
84      private final boolean directByDefault;
85      private final ByteBuf emptyBuf;
86  
87      /**
88       * Instance use heap buffers by default
89       */
90      protected AbstractByteBufAllocator() {
91          this(false);
92      }
93  
94      /**
95       * Create new instance
96       *
97       * @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
98       *                     a heap buffer
99       */
100     protected AbstractByteBufAllocator(boolean preferDirect) {
101         directByDefault = preferDirect && PlatformDependent.hasUnsafe();
102         emptyBuf = new EmptyByteBuf(this);
103     }
104 
105     @Override
106     public ByteBuf buffer() {
107         if (directByDefault) {
108             return directBuffer();
109         }
110         return heapBuffer();
111     }
112 
113     @Override
114     public ByteBuf buffer(int initialCapacity) {
115         if (directByDefault) {
116             return directBuffer(initialCapacity);
117         }
118         return heapBuffer(initialCapacity);
119     }
120 
121     @Override
122     public ByteBuf buffer(int initialCapacity, int maxCapacity) {
123         if (directByDefault) {
124             return directBuffer(initialCapacity, maxCapacity);
125         }
126         return heapBuffer(initialCapacity, maxCapacity);
127     }
128 
129     @Override
130     public ByteBuf ioBuffer() {
131         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
132             return directBuffer(DEFAULT_INITIAL_CAPACITY);
133         }
134         return heapBuffer(DEFAULT_INITIAL_CAPACITY);
135     }
136 
137     @Override
138     public ByteBuf ioBuffer(int initialCapacity) {
139         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
140             return directBuffer(initialCapacity);
141         }
142         return heapBuffer(initialCapacity);
143     }
144 
145     @Override
146     public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {
147         if (PlatformDependent.hasUnsafe() || isDirectBufferPooled()) {
148             return directBuffer(initialCapacity, maxCapacity);
149         }
150         return heapBuffer(initialCapacity, maxCapacity);
151     }
152 
153     @Override
154     public ByteBuf heapBuffer() {
155         return heapBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
156     }
157 
158     @Override
159     public ByteBuf heapBuffer(int initialCapacity) {
160         return heapBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
161     }
162 
163     @Override
164     public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
165         if (initialCapacity == 0 && maxCapacity == 0) {
166             return emptyBuf;
167         }
168         validate(initialCapacity, maxCapacity);
169         return newHeapBuffer(initialCapacity, maxCapacity);
170     }
171 
172     @Override
173     public ByteBuf directBuffer() {
174         return directBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
175     }
176 
177     @Override
178     public ByteBuf directBuffer(int initialCapacity) {
179         return directBuffer(initialCapacity, DEFAULT_MAX_CAPACITY);
180     }
181 
182     @Override
183     public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
184         if (initialCapacity == 0 && maxCapacity == 0) {
185             return emptyBuf;
186         }
187         validate(initialCapacity, maxCapacity);
188         return newDirectBuffer(initialCapacity, maxCapacity);
189     }
190 
191     @Override
192     public CompositeByteBuf compositeBuffer() {
193         if (directByDefault) {
194             return compositeDirectBuffer();
195         }
196         return compositeHeapBuffer();
197     }
198 
199     @Override
200     public CompositeByteBuf compositeBuffer(int maxNumComponents) {
201         if (directByDefault) {
202             return compositeDirectBuffer(maxNumComponents);
203         }
204         return compositeHeapBuffer(maxNumComponents);
205     }
206 
207     @Override
208     public CompositeByteBuf compositeHeapBuffer() {
209         return compositeHeapBuffer(DEFAULT_MAX_COMPONENTS);
210     }
211 
212     @Override
213     public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {
214         return toLeakAwareBuffer(new CompositeByteBuf(this, false, maxNumComponents));
215     }
216 
217     @Override
218     public CompositeByteBuf compositeDirectBuffer() {
219         return compositeDirectBuffer(DEFAULT_MAX_COMPONENTS);
220     }
221 
222     @Override
223     public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
224         return toLeakAwareBuffer(new CompositeByteBuf(this, true, maxNumComponents));
225     }
226 
227     private static void validate(int initialCapacity, int maxCapacity) {
228         checkPositiveOrZero(initialCapacity, "initialCapacity");
229         if (initialCapacity > maxCapacity) {
230             throw new IllegalArgumentException(String.format(
231                     "initialCapacity: %d (expected: not greater than maxCapacity(%d)",
232                     initialCapacity, maxCapacity));
233         }
234     }
235 
236     /**
237      * Create a heap {@link ByteBuf} with the given initialCapacity and maxCapacity.
238      */
239     protected abstract ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity);
240 
241     /**
242      * Create a direct {@link ByteBuf} with the given initialCapacity and maxCapacity.
243      */
244     protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);
245 
246     @Override
247     public String toString() {
248         return StringUtil.simpleClassName(this) + "(directByDefault: " + directByDefault + ')';
249     }
250 
251     @Override
252     public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
253         checkPositiveOrZero(minNewCapacity, "minNewCapacity");
254         if (minNewCapacity > maxCapacity) {
255             throw new IllegalArgumentException(String.format(
256                     "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
257                     minNewCapacity, maxCapacity));
258         }
259         final int threshold = CALCULATE_THRESHOLD; // 4 MiB page
260 
261         if (minNewCapacity == threshold) {
262             return threshold;
263         }
264 
265         // If over threshold, do not double but just increase by threshold.
266         if (minNewCapacity > threshold) {
267             int newCapacity = minNewCapacity / threshold * threshold;
268             if (newCapacity > maxCapacity - threshold) {
269                 newCapacity = maxCapacity;
270             } else {
271                 newCapacity += threshold;
272             }
273             return newCapacity;
274         }
275 
276         // 64 <= newCapacity is a power of 2 <= threshold
277         final int newCapacity = MathUtil.findNextPositivePowerOfTwo(Math.max(minNewCapacity, 64));
278         return Math.min(newCapacity, maxCapacity);
279     }
280 }