1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.buffer;
17
18 import static io.netty.buffer.PoolThreadCache.*;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 final class SizeClasses implements SizeClassesMetric {
82
83 static final int LOG2_QUANTUM = 4;
84
85 private static final int LOG2_SIZE_CLASS_GROUP = 2;
86 private static final int LOG2_MAX_LOOKUP_SIZE = 12;
87
88 private static final int LOG2GROUP_IDX = 1;
89 private static final int LOG2DELTA_IDX = 2;
90 private static final int NDELTA_IDX = 3;
91 private static final int PAGESIZE_IDX = 4;
92 private static final int SUBPAGE_IDX = 5;
93 private static final int LOG2_DELTA_LOOKUP_IDX = 6;
94
95 private static final byte no = 0, yes = 1;
96
97 final int pageSize;
98 final int pageShifts;
99 final int chunkSize;
100 final int directMemoryCacheAlignment;
101
102 final int nSizes;
103 final int nSubpages;
104 final int nPSizes;
105 final int lookupMaxSize;
106 final int smallMaxSizeIdx;
107
108 private final int[] pageIdx2sizeTab;
109
110
111 private final int[] sizeIdx2sizeTab;
112
113
114
115 private final int[] size2idxTab;
116
117 SizeClasses(int pageSize, int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
118 int group = log2(chunkSize) - LOG2_QUANTUM - LOG2_SIZE_CLASS_GROUP + 1;
119
120
121
122 short[][] sizeClasses = new short[group << LOG2_SIZE_CLASS_GROUP][7];
123
124 int normalMaxSize = -1;
125 int nSizes = 0;
126 int size = 0;
127
128 int log2Group = LOG2_QUANTUM;
129 int log2Delta = LOG2_QUANTUM;
130 int ndeltaLimit = 1 << LOG2_SIZE_CLASS_GROUP;
131
132
133
134 for (int nDelta = 0; nDelta < ndeltaLimit; nDelta++, nSizes++) {
135 short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, pageShifts);
136 sizeClasses[nSizes] = sizeClass;
137 size = sizeOf(sizeClass, directMemoryCacheAlignment);
138 }
139
140 log2Group += LOG2_SIZE_CLASS_GROUP;
141
142
143 for (; size < chunkSize; log2Group++, log2Delta++) {
144 for (int nDelta = 1; nDelta <= ndeltaLimit && size < chunkSize; nDelta++, nSizes++) {
145 short[] sizeClass = newSizeClass(nSizes, log2Group, log2Delta, nDelta, pageShifts);
146 sizeClasses[nSizes] = sizeClass;
147 size = normalMaxSize = sizeOf(sizeClass, directMemoryCacheAlignment);
148 }
149 }
150
151
152 assert chunkSize == normalMaxSize;
153
154 int smallMaxSizeIdx = 0;
155 int lookupMaxSize = 0;
156 int nPSizes = 0;
157 int nSubpages = 0;
158 for (int idx = 0; idx < nSizes; idx++) {
159 short[] sz = sizeClasses[idx];
160 if (sz[PAGESIZE_IDX] == yes) {
161 nPSizes++;
162 }
163 if (sz[SUBPAGE_IDX] == yes) {
164 nSubpages++;
165 smallMaxSizeIdx = idx;
166 }
167 if (sz[LOG2_DELTA_LOOKUP_IDX] != no) {
168 lookupMaxSize = sizeOf(sz, directMemoryCacheAlignment);
169 }
170 }
171 this.smallMaxSizeIdx = smallMaxSizeIdx;
172 this.lookupMaxSize = lookupMaxSize;
173 this.nPSizes = nPSizes;
174 this.nSubpages = nSubpages;
175 this.nSizes = nSizes;
176
177 this.pageSize = pageSize;
178 this.pageShifts = pageShifts;
179 this.chunkSize = chunkSize;
180 this.directMemoryCacheAlignment = directMemoryCacheAlignment;
181
182
183 this.sizeIdx2sizeTab = newIdx2SizeTab(sizeClasses, nSizes, directMemoryCacheAlignment);
184 this.pageIdx2sizeTab = newPageIdx2sizeTab(sizeClasses, nSizes, nPSizes, directMemoryCacheAlignment);
185 this.size2idxTab = newSize2idxTab(lookupMaxSize, sizeClasses);
186 }
187
188
189 private static short[] newSizeClass(int index, int log2Group, int log2Delta, int nDelta, int pageShifts) {
190 short isMultiPageSize;
191 if (log2Delta >= pageShifts) {
192 isMultiPageSize = yes;
193 } else {
194 int pageSize = 1 << pageShifts;
195 int size = calculateSize(log2Group, nDelta, log2Delta);
196
197 isMultiPageSize = size == size / pageSize * pageSize? yes : no;
198 }
199
200 int log2Ndelta = nDelta == 0? 0 : log2(nDelta);
201
202 byte remove = 1 << log2Ndelta < nDelta? yes : no;
203
204 int log2Size = log2Delta + log2Ndelta == log2Group? log2Group + 1 : log2Group;
205 if (log2Size == log2Group) {
206 remove = yes;
207 }
208
209 short isSubpage = log2Size < pageShifts + LOG2_SIZE_CLASS_GROUP? yes : no;
210
211 int log2DeltaLookup = log2Size < LOG2_MAX_LOOKUP_SIZE ||
212 log2Size == LOG2_MAX_LOOKUP_SIZE && remove == no
213 ? log2Delta : no;
214
215 return new short[] {
216 (short) index, (short) log2Group, (short) log2Delta,
217 (short) nDelta, isMultiPageSize, isSubpage, (short) log2DeltaLookup
218 };
219 }
220
221 private static int[] newIdx2SizeTab(short[][] sizeClasses, int nSizes, int directMemoryCacheAlignment) {
222 int[] sizeIdx2sizeTab = new int[nSizes];
223
224 for (int i = 0; i < nSizes; i++) {
225 short[] sizeClass = sizeClasses[i];
226 sizeIdx2sizeTab[i] = sizeOf(sizeClass, directMemoryCacheAlignment);
227 }
228 return sizeIdx2sizeTab;
229 }
230
231 private static int calculateSize(int log2Group, int nDelta, int log2Delta) {
232 return (1 << log2Group) + (nDelta << log2Delta);
233 }
234
235 private static int sizeOf(short[] sizeClass, int directMemoryCacheAlignment) {
236 int log2Group = sizeClass[LOG2GROUP_IDX];
237 int log2Delta = sizeClass[LOG2DELTA_IDX];
238 int nDelta = sizeClass[NDELTA_IDX];
239
240 int size = calculateSize(log2Group, nDelta, log2Delta);
241
242 return alignSizeIfNeeded(size, directMemoryCacheAlignment);
243 }
244
245 private static int[] newPageIdx2sizeTab(short[][] sizeClasses, int nSizes, int nPSizes,
246 int directMemoryCacheAlignment) {
247 int[] pageIdx2sizeTab = new int[nPSizes];
248 int pageIdx = 0;
249 for (int i = 0; i < nSizes; i++) {
250 short[] sizeClass = sizeClasses[i];
251 if (sizeClass[PAGESIZE_IDX] == yes) {
252 pageIdx2sizeTab[pageIdx++] = sizeOf(sizeClass, directMemoryCacheAlignment);
253 }
254 }
255 return pageIdx2sizeTab;
256 }
257
258 private static int[] newSize2idxTab(int lookupMaxSize, short[][] sizeClasses) {
259 int[] size2idxTab = new int[lookupMaxSize >> LOG2_QUANTUM];
260 int idx = 0;
261 int size = 0;
262
263 for (int i = 0; size <= lookupMaxSize; i++) {
264 int log2Delta = sizeClasses[i][LOG2DELTA_IDX];
265 int times = 1 << log2Delta - LOG2_QUANTUM;
266
267 while (size <= lookupMaxSize && times-- > 0) {
268 size2idxTab[idx++] = i;
269 size = idx + 1 << LOG2_QUANTUM;
270 }
271 }
272 return size2idxTab;
273 }
274
275 @Override
276 public int sizeIdx2size(int sizeIdx) {
277 return sizeIdx2sizeTab[sizeIdx];
278 }
279
280 @Override
281 public int sizeIdx2sizeCompute(int sizeIdx) {
282 int group = sizeIdx >> LOG2_SIZE_CLASS_GROUP;
283 int mod = sizeIdx & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
284
285 int groupSize = group == 0? 0 :
286 1 << LOG2_QUANTUM + LOG2_SIZE_CLASS_GROUP - 1 << group;
287
288 int shift = group == 0? 1 : group;
289 int lgDelta = shift + LOG2_QUANTUM - 1;
290 int modSize = mod + 1 << lgDelta;
291
292 return groupSize + modSize;
293 }
294
295 @Override
296 public long pageIdx2size(int pageIdx) {
297 return pageIdx2sizeTab[pageIdx];
298 }
299
300 @Override
301 public long pageIdx2sizeCompute(int pageIdx) {
302 int group = pageIdx >> LOG2_SIZE_CLASS_GROUP;
303 int mod = pageIdx & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
304
305 long groupSize = group == 0? 0 :
306 1L << pageShifts + LOG2_SIZE_CLASS_GROUP - 1 << group;
307
308 int shift = group == 0? 1 : group;
309 int log2Delta = shift + pageShifts - 1;
310 int modSize = mod + 1 << log2Delta;
311
312 return groupSize + modSize;
313 }
314
315 @Override
316 public int size2SizeIdx(int size) {
317 if (size == 0) {
318 return 0;
319 }
320 if (size > chunkSize) {
321 return nSizes;
322 }
323
324 size = alignSizeIfNeeded(size, directMemoryCacheAlignment);
325
326 if (size <= lookupMaxSize) {
327
328 return size2idxTab[size - 1 >> LOG2_QUANTUM];
329 }
330
331 int x = log2((size << 1) - 1);
332 int shift = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
333 ? 0 : x - (LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM);
334
335 int group = shift << LOG2_SIZE_CLASS_GROUP;
336
337 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
338 ? LOG2_QUANTUM : x - LOG2_SIZE_CLASS_GROUP - 1;
339
340 int mod = size - 1 >> log2Delta & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
341
342 return group + mod;
343 }
344
345 @Override
346 public int pages2pageIdx(int pages) {
347 return pages2pageIdxCompute(pages, false);
348 }
349
350 @Override
351 public int pages2pageIdxFloor(int pages) {
352 return pages2pageIdxCompute(pages, true);
353 }
354
355 private int pages2pageIdxCompute(int pages, boolean floor) {
356 int pageSize = pages << pageShifts;
357 if (pageSize > chunkSize) {
358 return nPSizes;
359 }
360
361 int x = log2((pageSize << 1) - 1);
362
363 int shift = x < LOG2_SIZE_CLASS_GROUP + pageShifts
364 ? 0 : x - (LOG2_SIZE_CLASS_GROUP + pageShifts);
365
366 int group = shift << LOG2_SIZE_CLASS_GROUP;
367
368 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + pageShifts + 1?
369 pageShifts : x - LOG2_SIZE_CLASS_GROUP - 1;
370
371 int mod = pageSize - 1 >> log2Delta & (1 << LOG2_SIZE_CLASS_GROUP) - 1;
372
373 int pageIdx = group + mod;
374
375 if (floor && pageIdx2sizeTab[pageIdx] > pages << pageShifts) {
376 pageIdx--;
377 }
378
379 return pageIdx;
380 }
381
382
383 private static int alignSizeIfNeeded(int size, int directMemoryCacheAlignment) {
384 if (directMemoryCacheAlignment <= 0) {
385 return size;
386 }
387 int delta = size & directMemoryCacheAlignment - 1;
388 return delta == 0? size : size + directMemoryCacheAlignment - delta;
389 }
390
391 @Override
392 public int normalizeSize(int size) {
393 if (size == 0) {
394 return sizeIdx2sizeTab[0];
395 }
396 size = alignSizeIfNeeded(size, directMemoryCacheAlignment);
397 if (size <= lookupMaxSize) {
398 int ret = sizeIdx2sizeTab[size2idxTab[size - 1 >> LOG2_QUANTUM]];
399 assert ret == normalizeSizeCompute(size);
400 return ret;
401 }
402 return normalizeSizeCompute(size);
403 }
404
405 private static int normalizeSizeCompute(int size) {
406 int x = log2((size << 1) - 1);
407 int log2Delta = x < LOG2_SIZE_CLASS_GROUP + LOG2_QUANTUM + 1
408 ? LOG2_QUANTUM : x - LOG2_SIZE_CLASS_GROUP - 1;
409 int delta = 1 << log2Delta;
410 int delta_mask = delta - 1;
411 return size + delta_mask & ~delta_mask;
412 }
413 }