// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.buffer;

import io.netty.util.internal.StringUtil;
import java.util.concurrent.atomic.LongAdder;
import java.nio.ByteBuffer;
import io.netty.util.internal.CleanableDirectBuffer;
import io.netty.util.internal.PlatformDependent;

public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator implements ByteBufAllocatorMetricProvider
{
    private final UnpooledByteBufAllocatorMetric metric;
    private final boolean disableLeakDetector;
    private final boolean noCleaner;
    public static final UnpooledByteBufAllocator DEFAULT;
    
    public UnpooledByteBufAllocator(final boolean preferDirect) {
        this(preferDirect, false);
    }
    
    public UnpooledByteBufAllocator(final boolean preferDirect, final boolean disableLeakDetector) {
        this(preferDirect, disableLeakDetector, PlatformDependent.useDirectBufferNoCleaner());
    }
    
    public UnpooledByteBufAllocator(final boolean preferDirect, final boolean disableLeakDetector, final boolean tryNoCleaner) {
        super(preferDirect);
        this.metric = new UnpooledByteBufAllocatorMetric();
        this.disableLeakDetector = disableLeakDetector;
        this.noCleaner = (tryNoCleaner && PlatformDependent.hasUnsafe() && PlatformDependent.hasDirectBufferNoCleanerConstructor());
    }
    
    @Override
    protected ByteBuf newHeapBuffer(final int initialCapacity, final int maxCapacity) {
        return PlatformDependent.hasUnsafe() ? new InstrumentedUnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) : new InstrumentedUnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }
    
    @Override
    protected ByteBuf newDirectBuffer(final int initialCapacity, final int maxCapacity) {
        ByteBuf buf;
        if (PlatformDependent.hasUnsafe()) {
            buf = (this.noCleaner ? new InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(this, initialCapacity, maxCapacity) : new InstrumentedUnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity));
        }
        else {
            buf = new InstrumentedUnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
        return this.disableLeakDetector ? buf : AbstractByteBufAllocator.toLeakAwareBuffer(buf);
    }
    
    @Override
    public CompositeByteBuf compositeHeapBuffer(final int maxNumComponents) {
        final CompositeByteBuf buf = new CompositeByteBuf(this, false, maxNumComponents);
        return this.disableLeakDetector ? buf : AbstractByteBufAllocator.toLeakAwareBuffer(buf);
    }
    
    @Override
    public CompositeByteBuf compositeDirectBuffer(final int maxNumComponents) {
        final CompositeByteBuf buf = new CompositeByteBuf(this, true, maxNumComponents);
        return this.disableLeakDetector ? buf : AbstractByteBufAllocator.toLeakAwareBuffer(buf);
    }
    
    @Override
    public boolean isDirectBufferPooled() {
        return false;
    }
    
    @Override
    public ByteBufAllocatorMetric metric() {
        return this.metric;
    }
    
    void incrementDirect(final int amount) {
        this.metric.directCounter.add(amount);
    }
    
    void decrementDirect(final int amount) {
        this.metric.directCounter.add(-amount);
    }
    
    void incrementHeap(final int amount) {
        this.metric.heapCounter.add(amount);
    }
    
    void decrementHeap(final int amount) {
        this.metric.heapCounter.add(-amount);
    }
    
    static {
        DEFAULT = new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());
    }
    
    private static final class InstrumentedUnpooledUnsafeHeapByteBuf extends UnpooledUnsafeHeapByteBuf
    {
        InstrumentedUnpooledUnsafeHeapByteBuf(final UnpooledByteBufAllocator alloc, final int initialCapacity, final int maxCapacity) {
            super(alloc, initialCapacity, maxCapacity);
        }
        
        @Override
        protected byte[] allocateArray(final int initialCapacity) {
            final byte[] bytes = super.allocateArray(initialCapacity);
            ((UnpooledByteBufAllocator)this.alloc()).incrementHeap(bytes.length);
            return bytes;
        }
        
        @Override
        protected void freeArray(final byte[] array) {
            final int length = array.length;
            super.freeArray(array);
            ((UnpooledByteBufAllocator)this.alloc()).decrementHeap(length);
        }
    }
    
    private static final class InstrumentedUnpooledHeapByteBuf extends UnpooledHeapByteBuf
    {
        InstrumentedUnpooledHeapByteBuf(final UnpooledByteBufAllocator alloc, final int initialCapacity, final int maxCapacity) {
            super(alloc, initialCapacity, maxCapacity);
        }
        
        @Override
        protected byte[] allocateArray(final int initialCapacity) {
            final byte[] bytes = super.allocateArray(initialCapacity);
            ((UnpooledByteBufAllocator)this.alloc()).incrementHeap(bytes.length);
            return bytes;
        }
        
        @Override
        protected void freeArray(final byte[] array) {
            final int length = array.length;
            super.freeArray(array);
            ((UnpooledByteBufAllocator)this.alloc()).decrementHeap(length);
        }
    }
    
    private static final class InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf extends UnpooledUnsafeNoCleanerDirectByteBuf
    {
        InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(final UnpooledByteBufAllocator alloc, final int initialCapacity, final int maxCapacity) {
            super(alloc, initialCapacity, maxCapacity, true);
        }
        
        @Override
        protected CleanableDirectBuffer allocateDirectBuffer(final int capacity) {
            final CleanableDirectBuffer buffer = super.allocateDirectBuffer(capacity);
            return new DecrementingCleanableDirectBuffer(this.alloc(), buffer);
        }
        
        @Override
        CleanableDirectBuffer reallocateDirect(final CleanableDirectBuffer oldBuffer, final int initialCapacity) {
            final int capacity = oldBuffer.buffer().capacity();
            final CleanableDirectBuffer buffer = super.reallocateDirect(oldBuffer, initialCapacity);
            return new DecrementingCleanableDirectBuffer(this.alloc(), buffer, buffer.buffer().capacity() - capacity);
        }
    }
    
    private static final class InstrumentedUnpooledUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf
    {
        InstrumentedUnpooledUnsafeDirectByteBuf(final UnpooledByteBufAllocator alloc, final int initialCapacity, final int maxCapacity) {
            super(alloc, initialCapacity, maxCapacity);
        }
        
        @Override
        protected CleanableDirectBuffer allocateDirectBuffer(final int capacity) {
            final CleanableDirectBuffer buffer = super.allocateDirectBuffer(capacity);
            return new DecrementingCleanableDirectBuffer(this.alloc(), buffer);
        }
        
        @Override
        protected ByteBuffer allocateDirect(final int initialCapacity) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        protected void freeDirect(final ByteBuffer buffer) {
            throw new UnsupportedOperationException();
        }
    }
    
    private static final class InstrumentedUnpooledDirectByteBuf extends UnpooledDirectByteBuf
    {
        InstrumentedUnpooledDirectByteBuf(final UnpooledByteBufAllocator alloc, final int initialCapacity, final int maxCapacity) {
            super(alloc, initialCapacity, maxCapacity);
        }
        
        @Override
        protected CleanableDirectBuffer allocateDirectBuffer(final int initialCapacity) {
            final CleanableDirectBuffer buffer = super.allocateDirectBuffer(initialCapacity);
            return new DecrementingCleanableDirectBuffer(this.alloc(), buffer);
        }
        
        @Override
        protected ByteBuffer allocateDirect(final int initialCapacity) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        protected void freeDirect(final ByteBuffer buffer) {
            throw new UnsupportedOperationException();
        }
    }
    
    private static final class DecrementingCleanableDirectBuffer implements CleanableDirectBuffer
    {
        private final UnpooledByteBufAllocator alloc;
        private final CleanableDirectBuffer delegate;
        
        private DecrementingCleanableDirectBuffer(final ByteBufAllocator alloc, final CleanableDirectBuffer delegate) {
            this(alloc, delegate, delegate.buffer().capacity());
        }
        
        private DecrementingCleanableDirectBuffer(final ByteBufAllocator alloc, final CleanableDirectBuffer delegate, final int capacityConsumed) {
            (this.alloc = (UnpooledByteBufAllocator)alloc).incrementDirect(capacityConsumed);
            this.delegate = delegate;
        }
        
        @Override
        public ByteBuffer buffer() {
            return this.delegate.buffer();
        }
        
        @Override
        public void clean() {
            final int capacity = this.delegate.buffer().capacity();
            this.delegate.clean();
            this.alloc.decrementDirect(capacity);
        }
        
        @Override
        public boolean hasMemoryAddress() {
            return this.delegate.hasMemoryAddress();
        }
        
        @Override
        public long memoryAddress() {
            return this.delegate.memoryAddress();
        }
    }
    
    private static final class UnpooledByteBufAllocatorMetric implements ByteBufAllocatorMetric
    {
        final LongAdder directCounter;
        final LongAdder heapCounter;
        
        private UnpooledByteBufAllocatorMetric() {
            this.directCounter = new LongAdder();
            this.heapCounter = new LongAdder();
        }
        
        @Override
        public long usedHeapMemory() {
            return this.heapCounter.sum();
        }
        
        @Override
        public long usedDirectMemory() {
            return this.directCounter.sum();
        }
        
        @Override
        public String toString() {
            return StringUtil.simpleClassName(this) + "(usedHeapMemory: " + this.usedHeapMemory() + "; usedDirectMemory: " + this.usedDirectMemory() + ')';
        }
    }
}
