1
16
17 package io.netty.buffer;
18
19 import static io.netty.buffer.PoolChunk.RUN_OFFSET_SHIFT;
20 import static io.netty.buffer.PoolChunk.SIZE_SHIFT;
21 import static io.netty.buffer.PoolChunk.IS_USED_SHIFT;
22 import static io.netty.buffer.PoolChunk.IS_SUBPAGE_SHIFT;
23 import static io.netty.buffer.SizeClasses.LOG2_QUANTUM;
24
25 final class PoolSubpage<T> implements PoolSubpageMetric {
26
27 final PoolChunk<T> chunk;
28 private final int pageShifts;
29 private final int runOffset;
30 private final int runSize;
31 private final long[] bitmap;
32
33 PoolSubpage<T> prev;
34 PoolSubpage<T> next;
35
36 boolean doNotDestroy;
37 int elemSize;
38 private int maxNumElems;
39 private int bitmapLength;
40 private int nextAvail;
41 private int numAvail;
42
43
44
45
46
47 PoolSubpage() {
48 chunk = null;
49 pageShifts = -1;
50 runOffset = -1;
51 elemSize = -1;
52 runSize = -1;
53 bitmap = null;
54 }
55
56 PoolSubpage(PoolSubpage<T> head, PoolChunk<T> chunk, int pageShifts, int runOffset, int runSize, int elemSize) {
57 this.chunk = chunk;
58 this.pageShifts = pageShifts;
59 this.runOffset = runOffset;
60 this.runSize = runSize;
61 this.elemSize = elemSize;
62 bitmap = new long[runSize >>> 6 + LOG2_QUANTUM];
63 init(head, elemSize);
64 }
65
66 void init(PoolSubpage<T> head, int elemSize) {
67 doNotDestroy = true;
68 if (elemSize != 0) {
69 maxNumElems = numAvail = runSize / elemSize;
70 nextAvail = 0;
71 bitmapLength = maxNumElems >>> 6;
72 if ((maxNumElems & 63) != 0) {
73 bitmapLength ++;
74 }
75
76 for (int i = 0; i < bitmapLength; i ++) {
77 bitmap[i] = 0;
78 }
79 }
80 addToPool(head);
81 }
82
83
86 long allocate() {
87 if (numAvail == 0 || !doNotDestroy) {
88 return -1;
89 }
90
91 final int bitmapIdx = getNextAvail();
92 int q = bitmapIdx >>> 6;
93 int r = bitmapIdx & 63;
94 assert (bitmap[q] >>> r & 1) == 0;
95 bitmap[q] |= 1L << r;
96
97 if (-- numAvail == 0) {
98 removeFromPool();
99 }
100
101 return toHandle(bitmapIdx);
102 }
103
104
108 boolean free(PoolSubpage<T> head, int bitmapIdx) {
109 if (elemSize == 0) {
110 return true;
111 }
112 int q = bitmapIdx >>> 6;
113 int r = bitmapIdx & 63;
114 assert (bitmap[q] >>> r & 1) != 0;
115 bitmap[q] ^= 1L << r;
116
117 setNextAvail(bitmapIdx);
118
119 if (numAvail ++ == 0) {
120 addToPool(head);
121 return true;
122 }
123
124 if (numAvail != maxNumElems) {
125 return true;
126 } else {
127
128 if (prev == next) {
129
130 return true;
131 }
132
133
134 doNotDestroy = false;
135 removeFromPool();
136 return false;
137 }
138 }
139
140 private void addToPool(PoolSubpage<T> head) {
141 assert prev == null && next == null;
142 prev = head;
143 next = head.next;
144 next.prev = this;
145 head.next = this;
146 }
147
148 private void removeFromPool() {
149 assert prev != null && next != null;
150 prev.next = next;
151 next.prev = prev;
152 next = null;
153 prev = null;
154 }
155
156 private void setNextAvail(int bitmapIdx) {
157 nextAvail = bitmapIdx;
158 }
159
160 private int getNextAvail() {
161 int nextAvail = this.nextAvail;
162 if (nextAvail >= 0) {
163 this.nextAvail = -1;
164 return nextAvail;
165 }
166 return findNextAvail();
167 }
168
169 private int findNextAvail() {
170 final long[] bitmap = this.bitmap;
171 final int bitmapLength = this.bitmapLength;
172 for (int i = 0; i < bitmapLength; i ++) {
173 long bits = bitmap[i];
174 if (~bits != 0) {
175 return findNextAvail0(i, bits);
176 }
177 }
178 return -1;
179 }
180
181 private int findNextAvail0(int i, long bits) {
182 final int maxNumElems = this.maxNumElems;
183 final int baseVal = i << 6;
184
185 for (int j = 0; j < 64; j ++) {
186 if ((bits & 1) == 0) {
187 int val = baseVal | j;
188 if (val < maxNumElems) {
189 return val;
190 } else {
191 break;
192 }
193 }
194 bits >>>= 1;
195 }
196 return -1;
197 }
198
199 private long toHandle(int bitmapIdx) {
200 int pages = runSize >> pageShifts;
201 return (long) runOffset << RUN_OFFSET_SHIFT
202 | (long) pages << SIZE_SHIFT
203 | 1L << IS_USED_SHIFT
204 | 1L << IS_SUBPAGE_SHIFT
205 | bitmapIdx;
206 }
207
208 @Override
209 public String toString() {
210 final boolean doNotDestroy;
211 final int maxNumElems;
212 final int numAvail;
213 final int elemSize;
214 if (chunk == null) {
215
216 doNotDestroy = true;
217 maxNumElems = 0;
218 numAvail = 0;
219 elemSize = -1;
220 } else {
221 synchronized (chunk.arena) {
222 if (!this.doNotDestroy) {
223 doNotDestroy = false;
224
225 maxNumElems = numAvail = elemSize = -1;
226 } else {
227 doNotDestroy = true;
228 maxNumElems = this.maxNumElems;
229 numAvail = this.numAvail;
230 elemSize = this.elemSize;
231 }
232 }
233 }
234
235 if (!doNotDestroy) {
236 return "(" + runOffset + ": not in use)";
237 }
238
239 return "(" + runOffset + ": " + (maxNumElems - numAvail) + '/' + maxNumElems +
240 ", offset: " + runOffset + ", length: " + runSize + ", elemSize: " + elemSize + ')';
241 }
242
243 @Override
244 public int maxNumElements() {
245 if (chunk == null) {
246
247 return 0;
248 }
249
250 synchronized (chunk.arena) {
251 return maxNumElems;
252 }
253 }
254
255 @Override
256 public int numAvailable() {
257 if (chunk == null) {
258
259 return 0;
260 }
261
262 synchronized (chunk.arena) {
263 return numAvail;
264 }
265 }
266
267 @Override
268 public int elementSize() {
269 if (chunk == null) {
270
271 return -1;
272 }
273
274 synchronized (chunk.arena) {
275 return elemSize;
276 }
277 }
278
279 @Override
280 public int pageSize() {
281 return 1 << pageShifts;
282 }
283
284 void destroy() {
285 if (chunk != null) {
286 chunk.destroy();
287 }
288 }
289 }
290