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