1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.buffer;
17
18 import io.netty.util.ByteProcessor;
19 import io.netty.util.CharsetUtil;
20 import io.netty.util.IllegalReferenceCountException;
21 import io.netty.util.NettyRuntime;
22 import io.netty.util.Recycler;
23 import io.netty.util.Recycler.EnhancedHandle;
24 import io.netty.util.ReferenceCounted;
25 import io.netty.util.concurrent.FastThreadLocal;
26 import io.netty.util.concurrent.FastThreadLocalThread;
27 import io.netty.util.concurrent.MpscIntQueue;
28 import io.netty.util.internal.AtomicReferenceCountUpdater;
29 import io.netty.util.internal.ObjectPool;
30 import io.netty.util.internal.ObjectUtil;
31 import io.netty.util.internal.PlatformDependent;
32 import io.netty.util.internal.ReferenceCountUpdater;
33 import io.netty.util.internal.SystemPropertyUtil;
34 import io.netty.util.internal.ThreadExecutorMap;
35 import io.netty.util.internal.UnsafeReferenceCountUpdater;
36 import io.netty.util.internal.UnstableApi;
37 import io.netty.util.internal.VarHandleReferenceCountUpdater;
38
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.lang.invoke.MethodHandles;
43 import java.lang.invoke.VarHandle;
44 import java.nio.ByteBuffer;
45 import java.nio.ByteOrder;
46 import java.nio.channels.ClosedChannelException;
47 import java.nio.channels.FileChannel;
48 import java.nio.channels.GatheringByteChannel;
49 import java.nio.channels.ScatteringByteChannel;
50 import java.nio.charset.Charset;
51 import java.util.Arrays;
52 import java.util.Queue;
53 import java.util.Set;
54 import java.util.concurrent.ConcurrentHashMap;
55 import java.util.concurrent.ConcurrentLinkedQueue;
56 import java.util.concurrent.ThreadLocalRandom;
57 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
58 import java.util.concurrent.atomic.AtomicLong;
59 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
60 import java.util.concurrent.locks.StampedLock;
61 import java.util.function.IntSupplier;
62
63 import static io.netty.util.internal.ReferenceCountUpdater.getUnsafeOffset;
64 import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 @UnstableApi
93 final class AdaptivePoolingAllocator {
94
95
96
97
98
99
100
101 static final int MIN_CHUNK_SIZE = 128 * 1024;
102 private static final int EXPANSION_ATTEMPTS = 3;
103 private static final int INITIAL_MAGAZINES = 1;
104 private static final int RETIRE_CAPACITY = 256;
105 private static final int MAX_STRIPES = NettyRuntime.availableProcessors() * 2;
106 private static final int BUFS_PER_CHUNK = 8;
107
108
109
110
111
112
113 private static final int MAX_CHUNK_SIZE = 8 * 1024 * 1024;
114 private static final int MAX_POOLED_BUF_SIZE = MAX_CHUNK_SIZE / BUFS_PER_CHUNK;
115
116
117
118
119
120
121 private static final int CHUNK_REUSE_QUEUE = Math.max(2, SystemPropertyUtil.getInt(
122 "io.netty.allocator.chunkReuseQueueCapacity", NettyRuntime.availableProcessors() * 2));
123
124
125
126
127
128 private static final int MAGAZINE_BUFFER_QUEUE_CAPACITY = SystemPropertyUtil.getInt(
129 "io.netty.allocator.magazineBufferQueueCapacity", 1024);
130
131
132
133
134
135
136
137
138
139
140
141
142
143 private static final int[] SIZE_CLASSES = {
144 32,
145 64,
146 128,
147 256,
148 512,
149 640,
150 1024,
151 1152,
152 2048,
153 2304,
154 4096,
155 4352,
156 8192,
157 8704,
158 16384,
159 16896,
160 };
161
162 private static final int SIZE_CLASSES_COUNT = SIZE_CLASSES.length;
163 private static final byte[] SIZE_INDEXES = new byte[(SIZE_CLASSES[SIZE_CLASSES_COUNT - 1] / 32) + 1];
164
165 static {
166 if (MAGAZINE_BUFFER_QUEUE_CAPACITY < 2) {
167 throw new IllegalArgumentException("MAGAZINE_BUFFER_QUEUE_CAPACITY: " + MAGAZINE_BUFFER_QUEUE_CAPACITY
168 + " (expected: >= " + 2 + ')');
169 }
170 int lastIndex = 0;
171 for (int i = 0; i < SIZE_CLASSES_COUNT; i++) {
172 int sizeClass = SIZE_CLASSES[i];
173
174 assert (sizeClass & 5) == 0 : "Size class must be a multiple of 32";
175 int sizeIndex = sizeIndexOf(sizeClass);
176 Arrays.fill(SIZE_INDEXES, lastIndex + 1, sizeIndex + 1, (byte) i);
177 lastIndex = sizeIndex;
178 }
179 }
180
181 private final ChunkAllocator chunkAllocator;
182 private final Set<Chunk> chunkRegistry;
183 private final MagazineGroup[] sizeClassedMagazineGroups;
184 private final MagazineGroup largeBufferMagazineGroup;
185 private final FastThreadLocal<MagazineGroup[]> threadLocalGroup;
186
187 AdaptivePoolingAllocator(ChunkAllocator chunkAllocator, boolean useCacheForNonEventLoopThreads) {
188 this.chunkAllocator = ObjectUtil.checkNotNull(chunkAllocator, "chunkAllocator");
189 chunkRegistry = ConcurrentHashMap.newKeySet();
190 sizeClassedMagazineGroups = createMagazineGroupSizeClasses(this, false);
191 largeBufferMagazineGroup = new MagazineGroup(
192 this, chunkAllocator, new HistogramChunkControllerFactory(true), false);
193
194 threadLocalGroup = new FastThreadLocal<MagazineGroup[]>() {
195 @Override
196 protected MagazineGroup[] initialValue() {
197 if (useCacheForNonEventLoopThreads || ThreadExecutorMap.currentExecutor() != null) {
198 return createMagazineGroupSizeClasses(AdaptivePoolingAllocator.this, true);
199 }
200 return null;
201 }
202
203 @Override
204 protected void onRemoval(final MagazineGroup[] groups) throws Exception {
205 if (groups != null) {
206 for (MagazineGroup group : groups) {
207 group.free();
208 }
209 }
210 }
211 };
212 }
213
214 private static MagazineGroup[] createMagazineGroupSizeClasses(
215 AdaptivePoolingAllocator allocator, boolean isThreadLocal) {
216 MagazineGroup[] groups = new MagazineGroup[SIZE_CLASSES.length];
217 for (int i = 0; i < SIZE_CLASSES.length; i++) {
218 int segmentSize = SIZE_CLASSES[i];
219 groups[i] = new MagazineGroup(allocator, allocator.chunkAllocator,
220 new SizeClassChunkControllerFactory(segmentSize), isThreadLocal);
221 }
222 return groups;
223 }
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245 private static Queue<Chunk> createSharedChunkQueue() {
246 return PlatformDependent.newFixedMpmcQueue(CHUNK_REUSE_QUEUE);
247 }
248
249 ByteBuf allocate(int size, int maxCapacity) {
250 return allocate(size, maxCapacity, Thread.currentThread(), null);
251 }
252
253 private AdaptiveByteBuf allocate(int size, int maxCapacity, Thread currentThread, AdaptiveByteBuf buf) {
254 AdaptiveByteBuf allocated = null;
255 if (size <= MAX_POOLED_BUF_SIZE) {
256 final int index = sizeClassIndexOf(size);
257 MagazineGroup[] magazineGroups;
258 if (!FastThreadLocalThread.currentThreadWillCleanupFastThreadLocals() ||
259 (magazineGroups = threadLocalGroup.get()) == null) {
260 magazineGroups = sizeClassedMagazineGroups;
261 }
262 if (index < magazineGroups.length) {
263 allocated = magazineGroups[index].allocate(size, maxCapacity, currentThread, buf);
264 } else {
265 allocated = largeBufferMagazineGroup.allocate(size, maxCapacity, currentThread, buf);
266 }
267 }
268 if (allocated == null) {
269 allocated = allocateFallback(size, maxCapacity, currentThread, buf);
270 }
271 return allocated;
272 }
273
274 private static int sizeIndexOf(final int size) {
275
276 return size + 31 >> 5;
277 }
278
279 static int sizeClassIndexOf(int size) {
280 int sizeIndex = sizeIndexOf(size);
281 if (sizeIndex < SIZE_INDEXES.length) {
282 return SIZE_INDEXES[sizeIndex];
283 }
284 return SIZE_CLASSES_COUNT;
285 }
286
287 static int[] getSizeClasses() {
288 return SIZE_CLASSES.clone();
289 }
290
291 private AdaptiveByteBuf allocateFallback(int size, int maxCapacity, Thread currentThread,
292 AdaptiveByteBuf buf) {
293
294 Magazine magazine;
295 if (buf != null) {
296 Chunk chunk = buf.chunk;
297 if (chunk == null || chunk == Magazine.MAGAZINE_FREED || (magazine = chunk.currentMagazine()) == null) {
298 magazine = getFallbackMagazine(currentThread);
299 }
300 } else {
301 magazine = getFallbackMagazine(currentThread);
302 buf = magazine.newBuffer();
303 }
304
305 AbstractByteBuf innerChunk = chunkAllocator.allocate(size, maxCapacity);
306 Chunk chunk = new Chunk(innerChunk, magazine, false, chunkSize -> true);
307 try {
308 chunk.readInitInto(buf, size, size, maxCapacity);
309 } finally {
310
311
312
313 chunk.release();
314 }
315 return buf;
316 }
317
318 private Magazine getFallbackMagazine(Thread currentThread) {
319 Magazine[] mags = largeBufferMagazineGroup.magazines;
320 return mags[(int) currentThread.getId() & mags.length - 1];
321 }
322
323
324
325
326 void reallocate(int size, int maxCapacity, AdaptiveByteBuf into) {
327 AdaptiveByteBuf result = allocate(size, maxCapacity, Thread.currentThread(), into);
328 assert result == into: "Re-allocation created separate buffer instance";
329 }
330
331 long usedMemory() {
332 long sum = 0;
333 for (Chunk chunk : chunkRegistry) {
334 sum += chunk.capacity();
335 }
336 return sum;
337 }
338
339
340
341
342 @SuppressWarnings({"FinalizeDeclaration", "deprecation"})
343 @Override
344 protected void finalize() throws Throwable {
345 try {
346 super.finalize();
347 } finally {
348 free();
349 }
350 }
351
352 private void free() {
353 largeBufferMagazineGroup.free();
354 }
355
356 static int sizeToBucket(int size) {
357 return HistogramChunkController.sizeToBucket(size);
358 }
359
360 private static final class MagazineGroup {
361 private final AdaptivePoolingAllocator allocator;
362 private final ChunkAllocator chunkAllocator;
363 private final ChunkControllerFactory chunkControllerFactory;
364 private final Queue<Chunk> chunkReuseQueue;
365 private final StampedLock magazineExpandLock;
366 private final Magazine threadLocalMagazine;
367 private volatile Magazine[] magazines;
368 private volatile boolean freed;
369
370 MagazineGroup(AdaptivePoolingAllocator allocator,
371 ChunkAllocator chunkAllocator,
372 ChunkControllerFactory chunkControllerFactory,
373 boolean isThreadLocal) {
374 this.allocator = allocator;
375 this.chunkAllocator = chunkAllocator;
376 this.chunkControllerFactory = chunkControllerFactory;
377 chunkReuseQueue = createSharedChunkQueue();
378 if (isThreadLocal) {
379 magazineExpandLock = null;
380 threadLocalMagazine = new Magazine(this, false, chunkReuseQueue, chunkControllerFactory.create(this));
381 } else {
382 magazineExpandLock = new StampedLock();
383 threadLocalMagazine = null;
384 Magazine[] mags = new Magazine[INITIAL_MAGAZINES];
385 for (int i = 0; i < mags.length; i++) {
386 mags[i] = new Magazine(this, true, chunkReuseQueue, chunkControllerFactory.create(this));
387 }
388 magazines = mags;
389 }
390 }
391
392 public AdaptiveByteBuf allocate(int size, int maxCapacity, Thread currentThread, AdaptiveByteBuf buf) {
393 boolean reallocate = buf != null;
394
395
396 Magazine tlMag = threadLocalMagazine;
397 if (tlMag != null) {
398 if (buf == null) {
399 buf = tlMag.newBuffer();
400 }
401 boolean allocated = tlMag.tryAllocate(size, maxCapacity, buf, reallocate);
402 assert allocated : "Allocation of threadLocalMagazine must always succeed";
403 return buf;
404 }
405
406
407 long threadId = currentThread.getId();
408 Magazine[] mags;
409 int expansions = 0;
410 do {
411 mags = magazines;
412 int mask = mags.length - 1;
413 int index = (int) (threadId & mask);
414 for (int i = 0, m = mags.length << 1; i < m; i++) {
415 Magazine mag = mags[index + i & mask];
416 if (buf == null) {
417 buf = mag.newBuffer();
418 }
419 if (mag.tryAllocate(size, maxCapacity, buf, reallocate)) {
420
421 return buf;
422 }
423 }
424 expansions++;
425 } while (expansions <= EXPANSION_ATTEMPTS && tryExpandMagazines(mags.length));
426
427
428 if (!reallocate && buf != null) {
429 buf.release();
430 }
431 return null;
432 }
433
434 private boolean tryExpandMagazines(int currentLength) {
435 if (currentLength >= MAX_STRIPES) {
436 return true;
437 }
438 final Magazine[] mags;
439 long writeLock = magazineExpandLock.tryWriteLock();
440 if (writeLock != 0) {
441 try {
442 mags = magazines;
443 if (mags.length >= MAX_STRIPES || mags.length > currentLength || freed) {
444 return true;
445 }
446 Magazine firstMagazine = mags[0];
447 Magazine[] expanded = new Magazine[mags.length * 2];
448 for (int i = 0, l = expanded.length; i < l; i++) {
449 Magazine m = new Magazine(this, true, chunkReuseQueue, chunkControllerFactory.create(this));
450 firstMagazine.initializeSharedStateIn(m);
451 expanded[i] = m;
452 }
453 magazines = expanded;
454 } finally {
455 magazineExpandLock.unlockWrite(writeLock);
456 }
457 for (Magazine magazine : mags) {
458 magazine.free();
459 }
460 }
461 return true;
462 }
463
464 boolean offerToQueue(Chunk buffer) {
465 if (freed) {
466 return false;
467 }
468
469 boolean isAdded = chunkReuseQueue.offer(buffer);
470 if (freed && isAdded) {
471
472 freeChunkReuseQueue();
473 }
474 return isAdded;
475 }
476
477 private void free() {
478 freed = true;
479 if (threadLocalMagazine != null) {
480 threadLocalMagazine.free();
481 } else {
482 long stamp = magazineExpandLock.writeLock();
483 try {
484 Magazine[] mags = magazines;
485 for (Magazine magazine : mags) {
486 magazine.free();
487 }
488 } finally {
489 magazineExpandLock.unlockWrite(stamp);
490 }
491 }
492 freeChunkReuseQueue();
493 }
494
495 private void freeChunkReuseQueue() {
496 for (;;) {
497 Chunk chunk = chunkReuseQueue.poll();
498 if (chunk == null) {
499 break;
500 }
501 chunk.release();
502 }
503 }
504 }
505
506 private interface ChunkControllerFactory {
507 ChunkController create(MagazineGroup group);
508 }
509
510 private interface ChunkController {
511
512
513
514 int computeBufferCapacity(int requestedSize, int maxCapacity, boolean isReallocation);
515
516
517
518
519 void initializeSharedStateIn(ChunkController chunkController);
520
521
522
523
524 Chunk newChunkAllocation(int promptingSize, Magazine magazine);
525 }
526
527 private interface ChunkReleasePredicate {
528 boolean shouldReleaseChunk(int chunkSize);
529 }
530
531 private static final class SizeClassChunkControllerFactory implements ChunkControllerFactory {
532
533
534
535 private static final int MIN_SEGMENTS_PER_CHUNK = 32;
536 private final int segmentSize;
537 private final int chunkSize;
538 private final int[] segmentOffsets;
539
540 private SizeClassChunkControllerFactory(int segmentSize) {
541 this.segmentSize = ObjectUtil.checkPositive(segmentSize, "segmentSize");
542 this.chunkSize = Math.max(MIN_CHUNK_SIZE, segmentSize * MIN_SEGMENTS_PER_CHUNK);
543 int segmentsCount = chunkSize / segmentSize;
544 this.segmentOffsets = new int[segmentsCount];
545 for (int i = 0; i < segmentsCount; i++) {
546 segmentOffsets[i] = i * segmentSize;
547 }
548 }
549
550 @Override
551 public ChunkController create(MagazineGroup group) {
552 return new SizeClassChunkController(group, segmentSize, chunkSize, segmentOffsets);
553 }
554 }
555
556 private static final class SizeClassChunkController implements ChunkController {
557
558 private final ChunkAllocator chunkAllocator;
559 private final int segmentSize;
560 private final int chunkSize;
561 private final Set<Chunk> chunkRegistry;
562 private final int[] segmentOffsets;
563
564 private SizeClassChunkController(MagazineGroup group, int segmentSize, int chunkSize, int[] segmentOffsets) {
565 chunkAllocator = group.chunkAllocator;
566 this.segmentSize = segmentSize;
567 this.chunkSize = chunkSize;
568 chunkRegistry = group.allocator.chunkRegistry;
569 this.segmentOffsets = segmentOffsets;
570 }
571
572 @Override
573 public int computeBufferCapacity(
574 int requestedSize, int maxCapacity, boolean isReallocation) {
575 return Math.min(segmentSize, maxCapacity);
576 }
577
578 @Override
579 public void initializeSharedStateIn(ChunkController chunkController) {
580
581 }
582
583 @Override
584 public Chunk newChunkAllocation(int promptingSize, Magazine magazine) {
585 AbstractByteBuf chunkBuffer = chunkAllocator.allocate(chunkSize, chunkSize);
586 assert chunkBuffer.capacity() == chunkSize;
587 SizeClassedChunk chunk = new SizeClassedChunk(chunkBuffer, magazine, true,
588 segmentSize, segmentOffsets, size -> false);
589 chunkRegistry.add(chunk);
590 return chunk;
591 }
592 }
593
594 private static final class HistogramChunkControllerFactory implements ChunkControllerFactory {
595 private final boolean shareable;
596
597 private HistogramChunkControllerFactory(boolean shareable) {
598 this.shareable = shareable;
599 }
600
601 @Override
602 public ChunkController create(MagazineGroup group) {
603 return new HistogramChunkController(group, shareable);
604 }
605 }
606
607 private static final class HistogramChunkController implements ChunkController, ChunkReleasePredicate {
608 private static final int MIN_DATUM_TARGET = 1024;
609 private static final int MAX_DATUM_TARGET = 65534;
610 private static final int INIT_DATUM_TARGET = 9;
611 private static final int HISTO_BUCKET_COUNT = 16;
612 private static final int[] HISTO_BUCKETS = {
613 16 * 1024,
614 24 * 1024,
615 32 * 1024,
616 48 * 1024,
617 64 * 1024,
618 96 * 1024,
619 128 * 1024,
620 192 * 1024,
621 256 * 1024,
622 384 * 1024,
623 512 * 1024,
624 768 * 1024,
625 1024 * 1024,
626 1792 * 1024,
627 2048 * 1024,
628 3072 * 1024
629 };
630
631 private final MagazineGroup group;
632 private final boolean shareable;
633 private final short[][] histos = {
634 new short[HISTO_BUCKET_COUNT], new short[HISTO_BUCKET_COUNT],
635 new short[HISTO_BUCKET_COUNT], new short[HISTO_BUCKET_COUNT],
636 };
637 private final Set<Chunk> chunkRegistry;
638 private short[] histo = histos[0];
639 private final int[] sums = new int[HISTO_BUCKET_COUNT];
640
641 private int histoIndex;
642 private int datumCount;
643 private int datumTarget = INIT_DATUM_TARGET;
644 private boolean hasHadRotation;
645 private volatile int sharedPrefChunkSize = MIN_CHUNK_SIZE;
646 private volatile int localPrefChunkSize = MIN_CHUNK_SIZE;
647 private volatile int localUpperBufSize;
648
649 private HistogramChunkController(MagazineGroup group, boolean shareable) {
650 this.group = group;
651 this.shareable = shareable;
652 chunkRegistry = group.allocator.chunkRegistry;
653 }
654
655 @Override
656 public int computeBufferCapacity(
657 int requestedSize, int maxCapacity, boolean isReallocation) {
658 if (!isReallocation) {
659
660
661 recordAllocationSize(requestedSize);
662 }
663
664
665
666 int startCapLimits;
667 if (requestedSize <= 32768) {
668 startCapLimits = 65536;
669 } else {
670 startCapLimits = requestedSize * 2;
671 }
672 int startingCapacity = Math.min(startCapLimits, localUpperBufSize);
673 startingCapacity = Math.max(requestedSize, Math.min(maxCapacity, startingCapacity));
674 return startingCapacity;
675 }
676
677 private void recordAllocationSize(int bufferSizeToRecord) {
678
679
680
681 if (bufferSizeToRecord == 0) {
682 return;
683 }
684 int bucket = sizeToBucket(bufferSizeToRecord);
685 histo[bucket]++;
686 if (datumCount++ == datumTarget) {
687 rotateHistograms();
688 }
689 }
690
691 static int sizeToBucket(int size) {
692 int index = binarySearchInsertionPoint(Arrays.binarySearch(HISTO_BUCKETS, size));
693 return index >= HISTO_BUCKETS.length ? HISTO_BUCKETS.length - 1 : index;
694 }
695
696 private static int binarySearchInsertionPoint(int index) {
697 if (index < 0) {
698 index = -(index + 1);
699 }
700 return index;
701 }
702
703 static int bucketToSize(int sizeBucket) {
704 return HISTO_BUCKETS[sizeBucket];
705 }
706
707 private void rotateHistograms() {
708 short[][] hs = histos;
709 for (int i = 0; i < HISTO_BUCKET_COUNT; i++) {
710 sums[i] = (hs[0][i] & 0xFFFF) + (hs[1][i] & 0xFFFF) + (hs[2][i] & 0xFFFF) + (hs[3][i] & 0xFFFF);
711 }
712 int sum = 0;
713 for (int count : sums) {
714 sum += count;
715 }
716 int targetPercentile = (int) (sum * 0.99);
717 int sizeBucket = 0;
718 for (; sizeBucket < sums.length; sizeBucket++) {
719 if (sums[sizeBucket] > targetPercentile) {
720 break;
721 }
722 targetPercentile -= sums[sizeBucket];
723 }
724 hasHadRotation = true;
725 int percentileSize = bucketToSize(sizeBucket);
726 int prefChunkSize = Math.max(percentileSize * BUFS_PER_CHUNK, MIN_CHUNK_SIZE);
727 localUpperBufSize = percentileSize;
728 localPrefChunkSize = prefChunkSize;
729 if (shareable) {
730 for (Magazine mag : group.magazines) {
731 HistogramChunkController statistics = (HistogramChunkController) mag.chunkController;
732 prefChunkSize = Math.max(prefChunkSize, statistics.localPrefChunkSize);
733 }
734 }
735 if (sharedPrefChunkSize != prefChunkSize) {
736
737 datumTarget = Math.max(datumTarget >> 1, MIN_DATUM_TARGET);
738 sharedPrefChunkSize = prefChunkSize;
739 } else {
740
741 datumTarget = Math.min(datumTarget << 1, MAX_DATUM_TARGET);
742 }
743
744 histoIndex = histoIndex + 1 & 3;
745 histo = histos[histoIndex];
746 datumCount = 0;
747 Arrays.fill(histo, (short) 0);
748 }
749
750
751
752
753
754
755
756
757
758 int preferredChunkSize() {
759 return sharedPrefChunkSize;
760 }
761
762 @Override
763 public void initializeSharedStateIn(ChunkController chunkController) {
764 HistogramChunkController statistics = (HistogramChunkController) chunkController;
765 int sharedPrefChunkSize = this.sharedPrefChunkSize;
766 statistics.localPrefChunkSize = sharedPrefChunkSize;
767 statistics.sharedPrefChunkSize = sharedPrefChunkSize;
768 }
769
770 @Override
771 public Chunk newChunkAllocation(int promptingSize, Magazine magazine) {
772 int size = Math.max(promptingSize * BUFS_PER_CHUNK, preferredChunkSize());
773 int minChunks = size / MIN_CHUNK_SIZE;
774 if (MIN_CHUNK_SIZE * minChunks < size) {
775
776
777
778
779 size = MIN_CHUNK_SIZE * (1 + minChunks);
780 }
781
782
783 size = Math.min(size, MAX_CHUNK_SIZE);
784
785
786 if (!hasHadRotation && sharedPrefChunkSize == MIN_CHUNK_SIZE) {
787 sharedPrefChunkSize = size;
788 }
789
790 ChunkAllocator chunkAllocator = group.chunkAllocator;
791 Chunk chunk = new Chunk(chunkAllocator.allocate(size, size), magazine, true, this);
792 chunkRegistry.add(chunk);
793 return chunk;
794 }
795
796 @Override
797 public boolean shouldReleaseChunk(int chunkSize) {
798 int preferredSize = preferredChunkSize();
799 int givenChunks = chunkSize / MIN_CHUNK_SIZE;
800 int preferredChunks = preferredSize / MIN_CHUNK_SIZE;
801 int deviation = Math.abs(givenChunks - preferredChunks);
802
803
804 return deviation != 0 &&
805 ThreadLocalRandom.current().nextDouble() * 20.0 < deviation;
806 }
807 }
808
809 private static final class Magazine {
810 private static final AtomicReferenceFieldUpdater<Magazine, Chunk> NEXT_IN_LINE;
811 static {
812 NEXT_IN_LINE = AtomicReferenceFieldUpdater.newUpdater(Magazine.class, Chunk.class, "nextInLine");
813 }
814 private static final Chunk MAGAZINE_FREED = new Chunk();
815
816 private static final Recycler<AdaptiveByteBuf> EVENT_LOOP_LOCAL_BUFFER_POOL = new Recycler<AdaptiveByteBuf>() {
817 @Override
818 protected AdaptiveByteBuf newObject(Handle<AdaptiveByteBuf> handle) {
819 return new AdaptiveByteBuf(handle);
820 }
821 };
822
823 private Chunk current;
824 @SuppressWarnings("unused")
825 private volatile Chunk nextInLine;
826 private final MagazineGroup group;
827 private final ChunkController chunkController;
828 private final AtomicLong usedMemory;
829 private final StampedLock allocationLock;
830 private final Queue<AdaptiveByteBuf> bufferQueue;
831 private final ObjectPool.Handle<AdaptiveByteBuf> handle;
832 private final Queue<Chunk> sharedChunkQueue;
833
834 Magazine(MagazineGroup group, boolean shareable, Queue<Chunk> sharedChunkQueue,
835 ChunkController chunkController) {
836 this.group = group;
837 this.chunkController = chunkController;
838
839 if (shareable) {
840
841 allocationLock = new StampedLock();
842 bufferQueue = PlatformDependent.newFixedMpmcQueue(MAGAZINE_BUFFER_QUEUE_CAPACITY);
843 handle = new ObjectPool.Handle<AdaptiveByteBuf>() {
844 @Override
845 public void recycle(AdaptiveByteBuf self) {
846 bufferQueue.offer(self);
847 }
848 };
849 } else {
850 allocationLock = null;
851 bufferQueue = null;
852 handle = null;
853 }
854 usedMemory = new AtomicLong();
855 this.sharedChunkQueue = sharedChunkQueue;
856 }
857
858 public boolean tryAllocate(int size, int maxCapacity, AdaptiveByteBuf buf, boolean reallocate) {
859 if (allocationLock == null) {
860
861 return allocate(size, maxCapacity, buf, reallocate);
862 }
863
864
865 long writeLock = allocationLock.tryWriteLock();
866 if (writeLock != 0) {
867 try {
868 return allocate(size, maxCapacity, buf, reallocate);
869 } finally {
870 allocationLock.unlockWrite(writeLock);
871 }
872 }
873 return allocateWithoutLock(size, maxCapacity, buf);
874 }
875
876 private boolean allocateWithoutLock(int size, int maxCapacity, AdaptiveByteBuf buf) {
877 Chunk curr = NEXT_IN_LINE.getAndSet(this, null);
878 if (curr == MAGAZINE_FREED) {
879
880 restoreMagazineFreed();
881 return false;
882 }
883 if (curr == null) {
884 curr = sharedChunkQueue.poll();
885 if (curr == null) {
886 return false;
887 }
888 curr.attachToMagazine(this);
889 }
890 boolean allocated = false;
891 int remainingCapacity = curr.remainingCapacity();
892 int startingCapacity = chunkController.computeBufferCapacity(
893 size, maxCapacity, true );
894 if (remainingCapacity >= size) {
895 curr.readInitInto(buf, size, Math.min(remainingCapacity, startingCapacity), maxCapacity);
896 allocated = true;
897 }
898 try {
899 if (remainingCapacity >= RETIRE_CAPACITY) {
900 transferToNextInLineOrRelease(curr);
901 curr = null;
902 }
903 } finally {
904 if (curr != null) {
905 curr.releaseFromMagazine();
906 }
907 }
908 return allocated;
909 }
910
911 private boolean allocate(int size, int maxCapacity, AdaptiveByteBuf buf, boolean reallocate) {
912 int startingCapacity = chunkController.computeBufferCapacity(size, maxCapacity, reallocate);
913 Chunk curr = current;
914 if (curr != null) {
915
916 int remainingCapacity = curr.remainingCapacity();
917 if (remainingCapacity > startingCapacity) {
918 curr.readInitInto(buf, size, startingCapacity, maxCapacity);
919
920 return true;
921 }
922
923
924
925 current = null;
926 if (remainingCapacity >= size) {
927 try {
928 curr.readInitInto(buf, size, remainingCapacity, maxCapacity);
929 return true;
930 } finally {
931 curr.releaseFromMagazine();
932 }
933 }
934
935
936 if (remainingCapacity < RETIRE_CAPACITY) {
937 curr.releaseFromMagazine();
938 } else {
939
940
941 transferToNextInLineOrRelease(curr);
942 }
943 }
944
945 assert current == null;
946
947
948
949
950
951
952
953
954 curr = NEXT_IN_LINE.getAndSet(this, null);
955 if (curr != null) {
956 if (curr == MAGAZINE_FREED) {
957
958 restoreMagazineFreed();
959 return false;
960 }
961
962 int remainingCapacity = curr.remainingCapacity();
963 if (remainingCapacity > startingCapacity) {
964
965 curr.readInitInto(buf, size, startingCapacity, maxCapacity);
966 current = curr;
967 return true;
968 }
969
970 if (remainingCapacity >= size) {
971
972
973 try {
974 curr.readInitInto(buf, size, remainingCapacity, maxCapacity);
975 return true;
976 } finally {
977
978
979 curr.releaseFromMagazine();
980 }
981 } else {
982
983 curr.releaseFromMagazine();
984 }
985 }
986
987
988 curr = sharedChunkQueue.poll();
989 if (curr == null) {
990 curr = chunkController.newChunkAllocation(size, this);
991 } else {
992 curr.attachToMagazine(this);
993
994 int remainingCapacity = curr.remainingCapacity();
995 if (remainingCapacity == 0 || remainingCapacity < size) {
996
997 if (remainingCapacity < RETIRE_CAPACITY) {
998 curr.releaseFromMagazine();
999 } else {
1000
1001
1002 transferToNextInLineOrRelease(curr);
1003 }
1004 curr = chunkController.newChunkAllocation(size, this);
1005 }
1006 }
1007
1008 current = curr;
1009 try {
1010 int remainingCapacity = curr.remainingCapacity();
1011 assert remainingCapacity >= size;
1012 if (remainingCapacity > startingCapacity) {
1013 curr.readInitInto(buf, size, startingCapacity, maxCapacity);
1014 curr = null;
1015 } else {
1016 curr.readInitInto(buf, size, remainingCapacity, maxCapacity);
1017 }
1018 } finally {
1019 if (curr != null) {
1020
1021
1022 curr.releaseFromMagazine();
1023 current = null;
1024 }
1025 }
1026 return true;
1027 }
1028
1029 private void restoreMagazineFreed() {
1030 Chunk next = NEXT_IN_LINE.getAndSet(this, MAGAZINE_FREED);
1031 if (next != null && next != MAGAZINE_FREED) {
1032
1033 next.releaseFromMagazine();
1034 }
1035 }
1036
1037 private void transferToNextInLineOrRelease(Chunk chunk) {
1038 if (NEXT_IN_LINE.compareAndSet(this, null, chunk)) {
1039 return;
1040 }
1041
1042 Chunk nextChunk = NEXT_IN_LINE.get(this);
1043 if (nextChunk != null && nextChunk != MAGAZINE_FREED
1044 && chunk.remainingCapacity() > nextChunk.remainingCapacity()) {
1045 if (NEXT_IN_LINE.compareAndSet(this, nextChunk, chunk)) {
1046 nextChunk.releaseFromMagazine();
1047 return;
1048 }
1049 }
1050
1051
1052
1053
1054 chunk.releaseFromMagazine();
1055 }
1056
1057 boolean trySetNextInLine(Chunk chunk) {
1058 return NEXT_IN_LINE.compareAndSet(this, null, chunk);
1059 }
1060
1061 void free() {
1062
1063 restoreMagazineFreed();
1064 long stamp = allocationLock != null ? allocationLock.writeLock() : 0;
1065 try {
1066 if (current != null) {
1067 current.releaseFromMagazine();
1068 current = null;
1069 }
1070 } finally {
1071 if (allocationLock != null) {
1072 allocationLock.unlockWrite(stamp);
1073 }
1074 }
1075 }
1076
1077 public AdaptiveByteBuf newBuffer() {
1078 AdaptiveByteBuf buf;
1079 if (handle == null) {
1080 buf = EVENT_LOOP_LOCAL_BUFFER_POOL.get();
1081 } else {
1082 buf = bufferQueue.poll();
1083 if (buf == null) {
1084 buf = new AdaptiveByteBuf(handle);
1085 }
1086 }
1087 buf.resetRefCnt();
1088 buf.discardMarks();
1089 return buf;
1090 }
1091
1092 boolean offerToQueue(Chunk chunk) {
1093 return group.offerToQueue(chunk);
1094 }
1095
1096 public void initializeSharedStateIn(Magazine other) {
1097 chunkController.initializeSharedStateIn(other.chunkController);
1098 }
1099 }
1100
1101 private static class Chunk implements ReferenceCounted, ChunkInfo {
1102 private static final long REFCNT_FIELD_OFFSET;
1103 private static final AtomicIntegerFieldUpdater<Chunk> AIF_UPDATER;
1104 private static final Object REFCNT_FIELD_VH;
1105 private static final ReferenceCountUpdater<Chunk> updater;
1106
1107 static {
1108 switch (ReferenceCountUpdater.updaterTypeOf(Chunk.class, "refCnt")) {
1109 case Atomic:
1110 AIF_UPDATER = newUpdater(Chunk.class, "refCnt");
1111 REFCNT_FIELD_OFFSET = -1;
1112 REFCNT_FIELD_VH = null;
1113 updater = new AtomicReferenceCountUpdater<Chunk>() {
1114 @Override
1115 protected AtomicIntegerFieldUpdater<Chunk> updater() {
1116 return AIF_UPDATER;
1117 }
1118 };
1119 break;
1120 case Unsafe:
1121 AIF_UPDATER = null;
1122 REFCNT_FIELD_OFFSET = getUnsafeOffset(Chunk.class, "refCnt");
1123 REFCNT_FIELD_VH = null;
1124 updater = new UnsafeReferenceCountUpdater<Chunk>() {
1125 @Override
1126 protected long refCntFieldOffset() {
1127 return REFCNT_FIELD_OFFSET;
1128 }
1129 };
1130 break;
1131 case VarHandle:
1132 AIF_UPDATER = null;
1133 REFCNT_FIELD_OFFSET = -1;
1134 REFCNT_FIELD_VH = PlatformDependent.findVarHandleOfIntField(MethodHandles.lookup(),
1135 Chunk.class, "refCnt");
1136 updater = new VarHandleReferenceCountUpdater<Chunk>() {
1137 @Override
1138 protected VarHandle varHandle() {
1139 return (VarHandle) REFCNT_FIELD_VH;
1140 }
1141 };
1142 break;
1143 default:
1144 throw new Error("Unknown updater type for Chunk");
1145 }
1146 }
1147
1148 protected final AbstractByteBuf delegate;
1149 protected Magazine magazine;
1150 private final AdaptivePoolingAllocator allocator;
1151 private final ChunkReleasePredicate chunkReleasePredicate;
1152 private final int capacity;
1153 private final boolean pooled;
1154 protected int allocatedBytes;
1155
1156
1157 @SuppressWarnings({"unused", "FieldMayBeFinal"})
1158 private volatile int refCnt;
1159
1160 Chunk() {
1161
1162 delegate = null;
1163 magazine = null;
1164 allocator = null;
1165 chunkReleasePredicate = null;
1166 capacity = 0;
1167 pooled = false;
1168 }
1169
1170 Chunk(AbstractByteBuf delegate, Magazine magazine, boolean pooled,
1171 ChunkReleasePredicate chunkReleasePredicate) {
1172 this.delegate = delegate;
1173 this.pooled = pooled;
1174 capacity = delegate.capacity();
1175 updater.setInitialValue(this);
1176 attachToMagazine(magazine);
1177
1178
1179 allocator = magazine.group.allocator;
1180
1181 this.chunkReleasePredicate = chunkReleasePredicate;
1182
1183 if (PlatformDependent.isJfrEnabled() && AllocateChunkEvent.isEventEnabled()) {
1184 AllocateChunkEvent event = new AllocateChunkEvent();
1185 if (event.shouldCommit()) {
1186 event.fill(this, AdaptiveByteBufAllocator.class);
1187 event.pooled = pooled;
1188 event.threadLocal = magazine.allocationLock == null;
1189 event.commit();
1190 }
1191 }
1192 }
1193
1194 Magazine currentMagazine() {
1195 return magazine;
1196 }
1197
1198 void detachFromMagazine() {
1199 if (magazine != null) {
1200 magazine.usedMemory.getAndAdd(-capacity);
1201 magazine = null;
1202 }
1203 }
1204
1205 void attachToMagazine(Magazine magazine) {
1206 assert this.magazine == null;
1207 this.magazine = magazine;
1208 magazine.usedMemory.getAndAdd(capacity);
1209 }
1210
1211 @Override
1212 public Chunk touch(Object hint) {
1213 return this;
1214 }
1215
1216 @Override
1217 public int refCnt() {
1218 return updater.refCnt(this);
1219 }
1220
1221 @Override
1222 public Chunk retain() {
1223 return updater.retain(this);
1224 }
1225
1226 @Override
1227 public Chunk retain(int increment) {
1228 return updater.retain(this, increment);
1229 }
1230
1231 @Override
1232 public Chunk touch() {
1233 return this;
1234 }
1235
1236 @Override
1237 public boolean release() {
1238 if (updater.release(this)) {
1239 deallocate();
1240 return true;
1241 }
1242 return false;
1243 }
1244
1245 @Override
1246 public boolean release(int decrement) {
1247 if (updater.release(this, decrement)) {
1248 deallocate();
1249 return true;
1250 }
1251 return false;
1252 }
1253
1254
1255
1256
1257 boolean releaseFromMagazine() {
1258 return release();
1259 }
1260
1261
1262
1263
1264 boolean releaseSegment(int ignoredSegmentId) {
1265 return release();
1266 }
1267
1268 private void deallocate() {
1269 Magazine mag = magazine;
1270 int chunkSize = delegate.capacity();
1271 if (!pooled || chunkReleasePredicate.shouldReleaseChunk(chunkSize) || mag == null) {
1272
1273
1274 detachFromMagazine();
1275 onRelease();
1276 allocator.chunkRegistry.remove(this);
1277 delegate.release();
1278 } else {
1279 updater.resetRefCnt(this);
1280 delegate.setIndex(0, 0);
1281 allocatedBytes = 0;
1282 if (!mag.trySetNextInLine(this)) {
1283
1284 detachFromMagazine();
1285 if (!mag.offerToQueue(this)) {
1286
1287
1288 boolean released = updater.release(this);
1289 onRelease();
1290 allocator.chunkRegistry.remove(this);
1291 delegate.release();
1292 assert released;
1293 } else {
1294 onReturn(false);
1295 }
1296 } else {
1297 onReturn(true);
1298 }
1299 }
1300 }
1301
1302 private void onReturn(boolean returnedToMagazine) {
1303 if (PlatformDependent.isJfrEnabled() && ReturnChunkEvent.isEventEnabled()) {
1304 ReturnChunkEvent event = new ReturnChunkEvent();
1305 if (event.shouldCommit()) {
1306 event.fill(this, AdaptiveByteBufAllocator.class);
1307 event.returnedToMagazine = returnedToMagazine;
1308 event.commit();
1309 }
1310 }
1311 }
1312
1313 private void onRelease() {
1314 if (PlatformDependent.isJfrEnabled() && FreeChunkEvent.isEventEnabled()) {
1315 FreeChunkEvent event = new FreeChunkEvent();
1316 if (event.shouldCommit()) {
1317 event.fill(this, AdaptiveByteBufAllocator.class);
1318 event.pooled = pooled;
1319 event.commit();
1320 }
1321 }
1322 }
1323
1324 public void readInitInto(AdaptiveByteBuf buf, int size, int startingCapacity, int maxCapacity) {
1325 int startIndex = allocatedBytes;
1326 allocatedBytes = startIndex + startingCapacity;
1327 Chunk chunk = this;
1328 chunk.retain();
1329 try {
1330 buf.init(delegate, chunk, 0, 0, startIndex, size, startingCapacity, maxCapacity);
1331 chunk = null;
1332 } finally {
1333 if (chunk != null) {
1334
1335
1336
1337 allocatedBytes = startIndex;
1338 chunk.release();
1339 }
1340 }
1341 }
1342
1343 public int remainingCapacity() {
1344 return capacity - allocatedBytes;
1345 }
1346
1347 @Override
1348 public int capacity() {
1349 return capacity;
1350 }
1351
1352 @Override
1353 public boolean isDirect() {
1354 return delegate.isDirect();
1355 }
1356
1357 @Override
1358 public long memoryAddress() {
1359 return delegate._memoryAddress();
1360 }
1361 }
1362
1363 private static final class SizeClassedChunk extends Chunk {
1364 private static final int FREE_LIST_EMPTY = -1;
1365 private final int segmentSize;
1366 private final MpscIntQueue freeList;
1367
1368 SizeClassedChunk(AbstractByteBuf delegate, Magazine magazine, boolean pooled, int segmentSize,
1369 int[] segmentOffsets, ChunkReleasePredicate shouldReleaseChunk) {
1370 super(delegate, magazine, pooled, shouldReleaseChunk);
1371 this.segmentSize = segmentSize;
1372 int segmentCount = segmentOffsets.length;
1373 assert delegate.capacity() / segmentSize == segmentCount;
1374 assert segmentCount > 0: "Chunk must have a positive number of segments";
1375 freeList = MpscIntQueue.create(segmentCount, FREE_LIST_EMPTY);
1376 freeList.fill(segmentCount, new IntSupplier() {
1377 int counter;
1378 @Override
1379 public int getAsInt() {
1380 return segmentOffsets[counter++];
1381 }
1382 });
1383 }
1384
1385 @Override
1386 public void readInitInto(AdaptiveByteBuf buf, int size, int startingCapacity, int maxCapacity) {
1387 int startIndex = freeList.poll();
1388 if (startIndex == FREE_LIST_EMPTY) {
1389 throw new IllegalStateException("Free list is empty");
1390 }
1391 allocatedBytes += segmentSize;
1392 Chunk chunk = this;
1393 chunk.retain();
1394 try {
1395 buf.init(delegate, chunk, 0, 0, startIndex, size, startingCapacity, maxCapacity);
1396 chunk = null;
1397 } finally {
1398 if (chunk != null) {
1399
1400
1401
1402 allocatedBytes -= segmentSize;
1403 chunk.releaseSegment(startIndex);
1404 }
1405 }
1406 }
1407
1408 @Override
1409 public int remainingCapacity() {
1410 int remainingCapacity = super.remainingCapacity();
1411 if (remainingCapacity > segmentSize) {
1412 return remainingCapacity;
1413 }
1414 int updatedRemainingCapacity = freeList.size() * segmentSize;
1415 if (updatedRemainingCapacity == remainingCapacity) {
1416 return remainingCapacity;
1417 }
1418
1419 allocatedBytes = capacity() - updatedRemainingCapacity;
1420 return updatedRemainingCapacity;
1421 }
1422
1423 @Override
1424 boolean releaseFromMagazine() {
1425
1426
1427 Magazine mag = magazine;
1428 detachFromMagazine();
1429 if (!mag.offerToQueue(this)) {
1430 return super.releaseFromMagazine();
1431 }
1432 return false;
1433 }
1434
1435 @Override
1436 boolean releaseSegment(int startIndex) {
1437 boolean released = release();
1438 boolean segmentReturned = freeList.offer(startIndex);
1439 assert segmentReturned: "Unable to return segment " + startIndex + " to free list";
1440 return released;
1441 }
1442 }
1443
1444 static final class AdaptiveByteBuf extends AbstractReferenceCountedByteBuf {
1445
1446 private final ObjectPool.Handle<AdaptiveByteBuf> handle;
1447
1448
1449 private int startIndex;
1450 private AbstractByteBuf rootParent;
1451 Chunk chunk;
1452 private int length;
1453 private int maxFastCapacity;
1454 private ByteBuffer tmpNioBuf;
1455 private boolean hasArray;
1456 private boolean hasMemoryAddress;
1457
1458 AdaptiveByteBuf(ObjectPool.Handle<AdaptiveByteBuf> recyclerHandle) {
1459 super(0);
1460 handle = ObjectUtil.checkNotNull(recyclerHandle, "recyclerHandle");
1461 }
1462
1463 void init(AbstractByteBuf unwrapped, Chunk wrapped, int readerIndex, int writerIndex,
1464 int startIndex, int size, int capacity, int maxCapacity) {
1465 this.startIndex = startIndex;
1466 chunk = wrapped;
1467 length = size;
1468 maxFastCapacity = capacity;
1469 maxCapacity(maxCapacity);
1470 setIndex0(readerIndex, writerIndex);
1471 hasArray = unwrapped.hasArray();
1472 hasMemoryAddress = unwrapped.hasMemoryAddress();
1473 rootParent = unwrapped;
1474 tmpNioBuf = null;
1475
1476 if (PlatformDependent.isJfrEnabled() && AllocateBufferEvent.isEventEnabled()) {
1477 AllocateBufferEvent event = new AllocateBufferEvent();
1478 if (event.shouldCommit()) {
1479 event.fill(this, AdaptiveByteBufAllocator.class);
1480 event.chunkPooled = wrapped.pooled;
1481 Magazine m = wrapped.magazine;
1482 event.chunkThreadLocal = m != null && m.allocationLock == null;
1483 event.commit();
1484 }
1485 }
1486 }
1487
1488 private AbstractByteBuf rootParent() {
1489 final AbstractByteBuf rootParent = this.rootParent;
1490 if (rootParent != null) {
1491 return rootParent;
1492 }
1493 throw new IllegalReferenceCountException();
1494 }
1495
1496 @Override
1497 public int capacity() {
1498 return length;
1499 }
1500
1501 @Override
1502 public int maxFastWritableBytes() {
1503 return Math.min(maxFastCapacity, maxCapacity()) - writerIndex;
1504 }
1505
1506 @Override
1507 public ByteBuf capacity(int newCapacity) {
1508 if (length <= newCapacity && newCapacity <= maxFastCapacity) {
1509 ensureAccessible();
1510 length = newCapacity;
1511 return this;
1512 }
1513 checkNewCapacity(newCapacity);
1514 if (newCapacity < capacity()) {
1515 length = newCapacity;
1516 trimIndicesToCapacity(newCapacity);
1517 return this;
1518 }
1519
1520 if (PlatformDependent.isJfrEnabled() && ReallocateBufferEvent.isEventEnabled()) {
1521 ReallocateBufferEvent event = new ReallocateBufferEvent();
1522 if (event.shouldCommit()) {
1523 event.fill(this, AdaptiveByteBufAllocator.class);
1524 event.newCapacity = newCapacity;
1525 event.commit();
1526 }
1527 }
1528
1529
1530 Chunk chunk = this.chunk;
1531 AdaptivePoolingAllocator allocator = chunk.allocator;
1532 int readerIndex = this.readerIndex;
1533 int writerIndex = this.writerIndex;
1534 int baseOldRootIndex = startIndex;
1535 int oldCapacity = length;
1536 AbstractByteBuf oldRoot = rootParent();
1537 allocator.reallocate(newCapacity, maxCapacity(), this);
1538 oldRoot.getBytes(baseOldRootIndex, this, 0, oldCapacity);
1539 chunk.releaseSegment(baseOldRootIndex);
1540 this.readerIndex = readerIndex;
1541 this.writerIndex = writerIndex;
1542 return this;
1543 }
1544
1545 @Override
1546 public ByteBufAllocator alloc() {
1547 return rootParent().alloc();
1548 }
1549
1550 @Override
1551 public ByteOrder order() {
1552 return rootParent().order();
1553 }
1554
1555 @Override
1556 public ByteBuf unwrap() {
1557 return null;
1558 }
1559
1560 @Override
1561 public boolean isDirect() {
1562 return rootParent().isDirect();
1563 }
1564
1565 @Override
1566 public int arrayOffset() {
1567 return idx(rootParent().arrayOffset());
1568 }
1569
1570 @Override
1571 public boolean hasMemoryAddress() {
1572 return hasMemoryAddress;
1573 }
1574
1575 @Override
1576 public long memoryAddress() {
1577 ensureAccessible();
1578 return _memoryAddress();
1579 }
1580
1581 @Override
1582 long _memoryAddress() {
1583 AbstractByteBuf root = rootParent;
1584 return root != null ? root._memoryAddress() + startIndex : 0L;
1585 }
1586
1587 @Override
1588 public ByteBuffer nioBuffer(int index, int length) {
1589 checkIndex(index, length);
1590 return rootParent().nioBuffer(idx(index), length);
1591 }
1592
1593 @Override
1594 public ByteBuffer internalNioBuffer(int index, int length) {
1595 checkIndex(index, length);
1596 return (ByteBuffer) internalNioBuffer().position(index).limit(index + length);
1597 }
1598
1599 private ByteBuffer internalNioBuffer() {
1600 if (tmpNioBuf == null) {
1601 tmpNioBuf = rootParent().nioBuffer(startIndex, maxFastCapacity);
1602 }
1603 return (ByteBuffer) tmpNioBuf.clear();
1604 }
1605
1606 @Override
1607 public ByteBuffer[] nioBuffers(int index, int length) {
1608 checkIndex(index, length);
1609 return rootParent().nioBuffers(idx(index), length);
1610 }
1611
1612 @Override
1613 public boolean hasArray() {
1614 return hasArray;
1615 }
1616
1617 @Override
1618 public byte[] array() {
1619 ensureAccessible();
1620 return rootParent().array();
1621 }
1622
1623 @Override
1624 public ByteBuf copy(int index, int length) {
1625 checkIndex(index, length);
1626 return rootParent().copy(idx(index), length);
1627 }
1628
1629 @Override
1630 public int nioBufferCount() {
1631 return rootParent().nioBufferCount();
1632 }
1633
1634 @Override
1635 protected byte _getByte(int index) {
1636 return rootParent()._getByte(idx(index));
1637 }
1638
1639 @Override
1640 protected short _getShort(int index) {
1641 return rootParent()._getShort(idx(index));
1642 }
1643
1644 @Override
1645 protected short _getShortLE(int index) {
1646 return rootParent()._getShortLE(idx(index));
1647 }
1648
1649 @Override
1650 protected int _getUnsignedMedium(int index) {
1651 return rootParent()._getUnsignedMedium(idx(index));
1652 }
1653
1654 @Override
1655 protected int _getUnsignedMediumLE(int index) {
1656 return rootParent()._getUnsignedMediumLE(idx(index));
1657 }
1658
1659 @Override
1660 protected int _getInt(int index) {
1661 return rootParent()._getInt(idx(index));
1662 }
1663
1664 @Override
1665 protected int _getIntLE(int index) {
1666 return rootParent()._getIntLE(idx(index));
1667 }
1668
1669 @Override
1670 protected long _getLong(int index) {
1671 return rootParent()._getLong(idx(index));
1672 }
1673
1674 @Override
1675 protected long _getLongLE(int index) {
1676 return rootParent()._getLongLE(idx(index));
1677 }
1678
1679 @Override
1680 public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) {
1681 checkIndex(index, length);
1682 rootParent().getBytes(idx(index), dst, dstIndex, length);
1683 return this;
1684 }
1685
1686 @Override
1687 public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) {
1688 checkIndex(index, length);
1689 rootParent().getBytes(idx(index), dst, dstIndex, length);
1690 return this;
1691 }
1692
1693 @Override
1694 public ByteBuf getBytes(int index, ByteBuffer dst) {
1695 checkIndex(index, dst.remaining());
1696 rootParent().getBytes(idx(index), dst);
1697 return this;
1698 }
1699
1700 @Override
1701 protected void _setByte(int index, int value) {
1702 rootParent()._setByte(idx(index), value);
1703 }
1704
1705 @Override
1706 protected void _setShort(int index, int value) {
1707 rootParent()._setShort(idx(index), value);
1708 }
1709
1710 @Override
1711 protected void _setShortLE(int index, int value) {
1712 rootParent()._setShortLE(idx(index), value);
1713 }
1714
1715 @Override
1716 protected void _setMedium(int index, int value) {
1717 rootParent()._setMedium(idx(index), value);
1718 }
1719
1720 @Override
1721 protected void _setMediumLE(int index, int value) {
1722 rootParent()._setMediumLE(idx(index), value);
1723 }
1724
1725 @Override
1726 protected void _setInt(int index, int value) {
1727 rootParent()._setInt(idx(index), value);
1728 }
1729
1730 @Override
1731 protected void _setIntLE(int index, int value) {
1732 rootParent()._setIntLE(idx(index), value);
1733 }
1734
1735 @Override
1736 protected void _setLong(int index, long value) {
1737 rootParent()._setLong(idx(index), value);
1738 }
1739
1740 @Override
1741 protected void _setLongLE(int index, long value) {
1742 rootParent().setLongLE(idx(index), value);
1743 }
1744
1745 @Override
1746 public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) {
1747 checkIndex(index, length);
1748 ByteBuffer tmp = (ByteBuffer) internalNioBuffer().clear().position(index);
1749 tmp.put(src, srcIndex, length);
1750 return this;
1751 }
1752
1753 @Override
1754 public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) {
1755 checkIndex(index, length);
1756 ByteBuffer tmp = (ByteBuffer) internalNioBuffer().clear().position(index);
1757 tmp.put(src.nioBuffer(srcIndex, length));
1758 return this;
1759 }
1760
1761 @Override
1762 public ByteBuf setBytes(int index, ByteBuffer src) {
1763 checkIndex(index, src.remaining());
1764 ByteBuffer tmp = (ByteBuffer) internalNioBuffer().clear().position(index);
1765 tmp.put(src);
1766 return this;
1767 }
1768
1769 @Override
1770 public ByteBuf getBytes(int index, OutputStream out, int length)
1771 throws IOException {
1772 checkIndex(index, length);
1773 if (length != 0) {
1774 ByteBufUtil.readBytes(alloc(), internalNioBuffer().duplicate(), index, length, out);
1775 }
1776 return this;
1777 }
1778
1779 @Override
1780 public int getBytes(int index, GatheringByteChannel out, int length)
1781 throws IOException {
1782 ByteBuffer buf = internalNioBuffer().duplicate();
1783 buf.clear().position(index).limit(index + length);
1784 return out.write(buf);
1785 }
1786
1787 @Override
1788 public int getBytes(int index, FileChannel out, long position, int length)
1789 throws IOException {
1790 ByteBuffer buf = internalNioBuffer().duplicate();
1791 buf.clear().position(index).limit(index + length);
1792 return out.write(buf, position);
1793 }
1794
1795 @Override
1796 public int setBytes(int index, InputStream in, int length)
1797 throws IOException {
1798 checkIndex(index, length);
1799 final AbstractByteBuf rootParent = rootParent();
1800 if (rootParent.hasArray()) {
1801 return rootParent.setBytes(idx(index), in, length);
1802 }
1803 byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
1804 int readBytes = in.read(tmp, 0, length);
1805 if (readBytes <= 0) {
1806 return readBytes;
1807 }
1808 setBytes(index, tmp, 0, readBytes);
1809 return readBytes;
1810 }
1811
1812 @Override
1813 public int setBytes(int index, ScatteringByteChannel in, int length)
1814 throws IOException {
1815 try {
1816 return in.read(internalNioBuffer(index, length).duplicate());
1817 } catch (ClosedChannelException ignored) {
1818 return -1;
1819 }
1820 }
1821
1822 @Override
1823 public int setBytes(int index, FileChannel in, long position, int length)
1824 throws IOException {
1825 try {
1826 return in.read(internalNioBuffer(index, length).duplicate(), position);
1827 } catch (ClosedChannelException ignored) {
1828 return -1;
1829 }
1830 }
1831
1832 @Override
1833 public int setCharSequence(int index, CharSequence sequence, Charset charset) {
1834 return setCharSequence0(index, sequence, charset, false);
1835 }
1836
1837 private int setCharSequence0(int index, CharSequence sequence, Charset charset, boolean expand) {
1838 if (charset.equals(CharsetUtil.UTF_8)) {
1839 int length = ByteBufUtil.utf8MaxBytes(sequence);
1840 if (expand) {
1841 ensureWritable0(length);
1842 checkIndex0(index, length);
1843 } else {
1844 checkIndex(index, length);
1845 }
1846
1847 return ByteBufUtil.writeUtf8(rootParent(), idx(index), length, sequence, sequence.length());
1848 }
1849 if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) {
1850 int length = sequence.length();
1851 if (expand) {
1852 ensureWritable0(length);
1853 checkIndex0(index, length);
1854 } else {
1855 checkIndex(index, length);
1856 }
1857
1858 return ByteBufUtil.writeAscii(rootParent(), idx(index), sequence, length);
1859 }
1860 byte[] bytes = sequence.toString().getBytes(charset);
1861 if (expand) {
1862 ensureWritable0(bytes.length);
1863
1864 }
1865 setBytes(index, bytes);
1866 return bytes.length;
1867 }
1868
1869 @Override
1870 public int writeCharSequence(CharSequence sequence, Charset charset) {
1871 int written = setCharSequence0(writerIndex, sequence, charset, true);
1872 writerIndex += written;
1873 return written;
1874 }
1875
1876 @Override
1877 public int forEachByte(int index, int length, ByteProcessor processor) {
1878 checkIndex(index, length);
1879 int ret = rootParent().forEachByte(idx(index), length, processor);
1880 return forEachResult(ret);
1881 }
1882
1883 @Override
1884 public int forEachByteDesc(int index, int length, ByteProcessor processor) {
1885 checkIndex(index, length);
1886 int ret = rootParent().forEachByteDesc(idx(index), length, processor);
1887 return forEachResult(ret);
1888 }
1889
1890 @Override
1891 public ByteBuf setZero(int index, int length) {
1892 checkIndex(index, length);
1893 rootParent().setZero(idx(index), length);
1894 return this;
1895 }
1896
1897 @Override
1898 public ByteBuf writeZero(int length) {
1899 ensureWritable(length);
1900 rootParent().setZero(idx(writerIndex), length);
1901 writerIndex += length;
1902 return this;
1903 }
1904
1905 private int forEachResult(int ret) {
1906 if (ret < startIndex) {
1907 return -1;
1908 }
1909 return ret - startIndex;
1910 }
1911
1912 @Override
1913 public boolean isContiguous() {
1914 return rootParent().isContiguous();
1915 }
1916
1917 private int idx(int index) {
1918 return index + startIndex;
1919 }
1920
1921 @Override
1922 protected void deallocate() {
1923 if (PlatformDependent.isJfrEnabled() && FreeBufferEvent.isEventEnabled()) {
1924 FreeBufferEvent event = new FreeBufferEvent();
1925 if (event.shouldCommit()) {
1926 event.fill(this, AdaptiveByteBufAllocator.class);
1927 event.commit();
1928 }
1929 }
1930
1931 if (chunk != null) {
1932 chunk.releaseSegment(startIndex);
1933 }
1934 tmpNioBuf = null;
1935 chunk = null;
1936 rootParent = null;
1937 if (handle instanceof EnhancedHandle) {
1938 EnhancedHandle<AdaptiveByteBuf> enhancedHandle = (EnhancedHandle<AdaptiveByteBuf>) handle;
1939 enhancedHandle.unguardedRecycle(this);
1940 } else {
1941 handle.recycle(this);
1942 }
1943 }
1944 }
1945
1946
1947
1948
1949 interface ChunkAllocator {
1950
1951
1952
1953
1954
1955
1956 AbstractByteBuf allocate(int initialCapacity, int maxCapacity);
1957 }
1958 }