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

package io.netty.util;

import io.netty.util.internal.ObjectPool;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.jetbrains.annotations.VisibleForTesting;
import io.netty.util.concurrent.FastThreadLocalThread;
import io.netty.util.internal.PlatformDependent;
import java.util.Objects;
import io.netty.util.internal.shaded.org.jctools.queues.MessagePassingQueue;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.logging.InternalLogger;

public abstract class Recycler<T>
{
    private static final InternalLogger logger;
    private static final EnhancedHandle<?> NOOP_HANDLE;
    private static final UnguardedLocalPool<?> NOOP_LOCAL_POOL;
    private static final int DEFAULT_INITIAL_MAX_CAPACITY_PER_THREAD = 4096;
    private static final int DEFAULT_MAX_CAPACITY_PER_THREAD;
    private static final int RATIO;
    private static final int DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD;
    private static final boolean BLOCKING_POOL;
    private static final boolean BATCH_FAST_TL_ONLY;
    private final LocalPool<?, T> localPool;
    private final FastThreadLocal<LocalPool<?, T>> threadLocalPool;
    
    protected Recycler(int maxCapacity, final boolean unguarded) {
        if (maxCapacity <= 0) {
            maxCapacity = 0;
        }
        else {
            maxCapacity = Math.max(4, maxCapacity);
        }
        this.threadLocalPool = null;
        if (maxCapacity == 0) {
            this.localPool = (LocalPool<?, T>)Recycler.NOOP_LOCAL_POOL;
        }
        else {
            this.localPool = (LocalPool<?, T>)(unguarded ? new UnguardedLocalPool(maxCapacity) : new GuardedLocalPool(maxCapacity));
        }
    }
    
    protected Recycler(final boolean unguarded) {
        this(Recycler.DEFAULT_MAX_CAPACITY_PER_THREAD, Recycler.RATIO, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD, unguarded);
    }
    
    protected Recycler(final Thread owner, final boolean unguarded) {
        this(Recycler.DEFAULT_MAX_CAPACITY_PER_THREAD, Recycler.RATIO, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD, owner, unguarded);
    }
    
    protected Recycler(final int maxCapacityPerThread) {
        this(maxCapacityPerThread, Recycler.RATIO, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD);
    }
    
    protected Recycler() {
        this(Recycler.DEFAULT_MAX_CAPACITY_PER_THREAD);
    }
    
    protected Recycler(final int chunksSize, final int maxCapacityPerThread, final boolean unguarded) {
        this(maxCapacityPerThread, Recycler.RATIO, chunksSize, unguarded);
    }
    
    protected Recycler(final int chunkSize, final int maxCapacityPerThread, final Thread owner, final boolean unguarded) {
        this(maxCapacityPerThread, Recycler.RATIO, chunkSize, owner, unguarded);
    }
    
    @Deprecated
    protected Recycler(final int maxCapacityPerThread, final int maxSharedCapacityFactor) {
        this(maxCapacityPerThread, Recycler.RATIO, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD);
    }
    
    @Deprecated
    protected Recycler(final int maxCapacityPerThread, final int maxSharedCapacityFactor, final int ratio, final int maxDelayedQueuesPerThread) {
        this(maxCapacityPerThread, ratio, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD);
    }
    
    @Deprecated
    protected Recycler(final int maxCapacityPerThread, final int maxSharedCapacityFactor, final int ratio, final int maxDelayedQueuesPerThread, final int delayedQueueRatio) {
        this(maxCapacityPerThread, ratio, Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD);
    }
    
    protected Recycler(final int maxCapacityPerThread, final int interval, final int chunkSize) {
        this(maxCapacityPerThread, interval, chunkSize, true, null, false);
    }
    
    protected Recycler(final int maxCapacityPerThread, final int interval, final int chunkSize, final boolean unguarded) {
        this(maxCapacityPerThread, interval, chunkSize, true, null, unguarded);
    }
    
    protected Recycler(final int maxCapacityPerThread, final int interval, final int chunkSize, final Thread owner, final boolean unguarded) {
        this(maxCapacityPerThread, interval, chunkSize, false, owner, unguarded);
    }
    
    private Recycler(int maxCapacityPerThread, final int ratio, int chunkSize, final boolean useThreadLocalStorage, final Thread owner, final boolean unguarded) {
        final int interval = Math.max(0, ratio);
        if (maxCapacityPerThread <= 0) {
            maxCapacityPerThread = 0;
            chunkSize = 0;
        }
        else {
            maxCapacityPerThread = Math.max(4, maxCapacityPerThread);
            chunkSize = Math.max(2, Math.min(chunkSize, maxCapacityPerThread >> 1));
        }
        if (maxCapacityPerThread > 0 && useThreadLocalStorage) {
            final int finalMaxCapacityPerThread = maxCapacityPerThread;
            final int finalChunkSize = chunkSize;
            this.threadLocalPool = new FastThreadLocal<LocalPool<?, T>>() {
                @Override
                protected LocalPool<?, T> initialValue() {
                    return (LocalPool<?, T>)(unguarded ? new UnguardedLocalPool(finalMaxCapacityPerThread, interval, finalChunkSize) : new GuardedLocalPool(finalMaxCapacityPerThread, interval, finalChunkSize));
                }
                
                @Override
                protected void onRemoval(final LocalPool<?, T> value) throws Exception {
                    super.onRemoval(value);
                    final MessagePassingQueue<?> handles = ((LocalPool<Object, Object>)value).pooledHandles;
                    ((LocalPool<Object, Object>)value).pooledHandles = null;
                    ((LocalPool<Object, Object>)value).owner = null;
                    if (handles != null) {
                        handles.clear();
                    }
                }
            };
            this.localPool = null;
        }
        else {
            this.threadLocalPool = null;
            if (maxCapacityPerThread == 0) {
                this.localPool = (LocalPool<?, T>)Recycler.NOOP_LOCAL_POOL;
            }
            else {
                Objects.requireNonNull(owner, "owner");
                this.localPool = (LocalPool<?, T>)(unguarded ? new UnguardedLocalPool(owner, maxCapacityPerThread, interval, chunkSize) : new GuardedLocalPool(owner, maxCapacityPerThread, interval, chunkSize));
            }
        }
    }
    
    public final T get() {
        if (this.localPool != null) {
            return this.localPool.getWith(this);
        }
        if (PlatformDependent.isVirtualThread(Thread.currentThread()) && !FastThreadLocalThread.currentThreadHasFastThreadLocal()) {
            return (T)this.newObject(Recycler.NOOP_HANDLE);
        }
        return (T)this.threadLocalPool.get().getWith(this);
    }
    
    public static void unpinOwner(final Recycler<?> recycler) {
        if (recycler.localPool != null) {
            ((LocalPool<Object, Object>)recycler.localPool).owner = null;
        }
    }
    
    @Deprecated
    public final boolean recycle(final T o, final Handle<T> handle) {
        if (handle == Recycler.NOOP_HANDLE) {
            return false;
        }
        handle.recycle(o);
        return true;
    }
    
    @VisibleForTesting
    final int threadLocalSize() {
        if (this.localPool != null) {
            return this.localPool.size();
        }
        if (PlatformDependent.isVirtualThread(Thread.currentThread()) && !FastThreadLocalThread.currentThreadHasFastThreadLocal()) {
            return 0;
        }
        final LocalPool<?, T> pool = this.threadLocalPool.getIfExists();
        if (pool == null) {
            return 0;
        }
        return pool.size();
    }
    
    protected abstract T newObject(final Handle<T> p0);
    
    static {
        logger = InternalLoggerFactory.getInstance(Recycler.class);
        NOOP_HANDLE = new LocalPoolHandle<Object>((UnguardedLocalPool)null);
        NOOP_LOCAL_POOL = new UnguardedLocalPool<Object>(0);
        int maxCapacityPerThread = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacityPerThread", SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", 4096));
        if (maxCapacityPerThread < 0) {
            maxCapacityPerThread = 4096;
        }
        DEFAULT_MAX_CAPACITY_PER_THREAD = maxCapacityPerThread;
        DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD = SystemPropertyUtil.getInt("io.netty.recycler.chunkSize", 32);
        RATIO = Math.max(0, SystemPropertyUtil.getInt("io.netty.recycler.ratio", 8));
        BLOCKING_POOL = SystemPropertyUtil.getBoolean("io.netty.recycler.blocking", false);
        BATCH_FAST_TL_ONLY = SystemPropertyUtil.getBoolean("io.netty.recycler.batchFastThreadLocalOnly", true);
        if (Recycler.logger.isDebugEnabled()) {
            if (Recycler.DEFAULT_MAX_CAPACITY_PER_THREAD == 0) {
                Recycler.logger.debug("-Dio.netty.recycler.maxCapacityPerThread: disabled");
                Recycler.logger.debug("-Dio.netty.recycler.ratio: disabled");
                Recycler.logger.debug("-Dio.netty.recycler.chunkSize: disabled");
                Recycler.logger.debug("-Dio.netty.recycler.blocking: disabled");
                Recycler.logger.debug("-Dio.netty.recycler.batchFastThreadLocalOnly: disabled");
            }
            else {
                Recycler.logger.debug("-Dio.netty.recycler.maxCapacityPerThread: {}", (Object)Recycler.DEFAULT_MAX_CAPACITY_PER_THREAD);
                Recycler.logger.debug("-Dio.netty.recycler.ratio: {}", (Object)Recycler.RATIO);
                Recycler.logger.debug("-Dio.netty.recycler.chunkSize: {}", (Object)Recycler.DEFAULT_QUEUE_CHUNK_SIZE_PER_THREAD);
                Recycler.logger.debug("-Dio.netty.recycler.blocking: {}", (Object)Recycler.BLOCKING_POOL);
                Recycler.logger.debug("-Dio.netty.recycler.batchFastThreadLocalOnly: {}", (Object)Recycler.BATCH_FAST_TL_ONLY);
            }
        }
    }
    
    private static final class LocalPoolHandle<T> extends EnhancedHandle<T>
    {
        private final UnguardedLocalPool<T> pool;
        
        private LocalPoolHandle(final UnguardedLocalPool<T> pool) {
            this.pool = pool;
        }
        
        @Override
        public void recycle(final T object) {
            final UnguardedLocalPool<T> pool = this.pool;
            if (pool != null) {
                pool.release((T)object);
            }
        }
        
        @Override
        public void unguardedRecycle(final Object object) {
            final UnguardedLocalPool<T> pool = this.pool;
            if (pool != null) {
                pool.release((T)object);
            }
        }
    }
    
    public abstract static class EnhancedHandle<T> implements Handle<T>
    {
        public abstract void unguardedRecycle(final Object p0);
        
        private EnhancedHandle() {
        }
    }
    
    private static final class DefaultHandle<T> extends EnhancedHandle<T>
    {
        private static final int STATE_CLAIMED = 0;
        private static final int STATE_AVAILABLE = 1;
        private static final AtomicIntegerFieldUpdater<DefaultHandle<?>> STATE_UPDATER;
        private volatile int state;
        private final GuardedLocalPool<T> localPool;
        private T value;
        
        DefaultHandle(final GuardedLocalPool<T> localPool) {
            this.localPool = localPool;
        }
        
        @Override
        public void recycle(final Object object) {
            if (object != this.value) {
                throw new IllegalArgumentException("object does not belong to handle");
            }
            this.toAvailable();
            this.localPool.release((DefaultHandle<T>)this);
        }
        
        @Override
        public void unguardedRecycle(final Object object) {
            if (object != this.value) {
                throw new IllegalArgumentException("object does not belong to handle");
            }
            this.unguardedToAvailable();
            this.localPool.release((DefaultHandle<T>)this);
        }
        
        T claim() {
            assert this.state == 1;
            DefaultHandle.STATE_UPDATER.lazySet(this, 0);
            return this.value;
        }
        
        void set(final T value) {
            this.value = value;
        }
        
        private void toAvailable() {
            final int prev = DefaultHandle.STATE_UPDATER.getAndSet(this, 1);
            if (prev == 1) {
                throw new IllegalStateException("Object has been recycled already.");
            }
        }
        
        private void unguardedToAvailable() {
            final int prev = this.state;
            if (prev == 1) {
                throw new IllegalStateException("Object has been recycled already.");
            }
            DefaultHandle.STATE_UPDATER.lazySet(this, 1);
        }
        
        static {
            final AtomicIntegerFieldUpdater<?> updater = STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater((Class<?>)DefaultHandle.class, "state");
        }
    }
    
    private static final class GuardedLocalPool<T> extends LocalPool<DefaultHandle<T>, T>
    {
        GuardedLocalPool(final int maxCapacity) {
            super(maxCapacity);
        }
        
        GuardedLocalPool(final Thread owner, final int maxCapacity, final int ratioInterval, final int chunkSize) {
            super(owner, maxCapacity, ratioInterval, chunkSize);
        }
        
        GuardedLocalPool(final int maxCapacity, final int ratioInterval, final int chunkSize) {
            super(maxCapacity, ratioInterval, chunkSize);
        }
        
        public T getWith(final Recycler<T> recycler) {
            DefaultHandle<T> handle = this.acquire();
            T obj;
            if (handle == null) {
                handle = (this.canAllocatePooled() ? new DefaultHandle<T>(this) : null);
                if (handle != null) {
                    obj = recycler.newObject(handle);
                    handle.set(obj);
                }
                else {
                    obj = recycler.newObject(Recycler.NOOP_HANDLE);
                }
            }
            else {
                obj = handle.claim();
            }
            return obj;
        }
    }
    
    private static final class UnguardedLocalPool<T> extends LocalPool<T, T>
    {
        private final EnhancedHandle<T> handle;
        
        UnguardedLocalPool(final int maxCapacity) {
            super(maxCapacity);
            this.handle = ((maxCapacity == 0) ? null : new LocalPoolHandle<T>(this));
        }
        
        UnguardedLocalPool(final Thread owner, final int maxCapacity, final int ratioInterval, final int chunkSize) {
            super(owner, maxCapacity, ratioInterval, chunkSize);
            this.handle = new LocalPoolHandle<T>(this);
        }
        
        UnguardedLocalPool(final int maxCapacity, final int ratioInterval, final int chunkSize) {
            super(maxCapacity, ratioInterval, chunkSize);
            this.handle = new LocalPoolHandle<T>(this);
        }
        
        public T getWith(final Recycler<T> recycler) {
            T obj = this.acquire();
            if (obj == null) {
                obj = recycler.newObject(this.canAllocatePooled() ? this.handle : Recycler.NOOP_HANDLE);
            }
            return obj;
        }
    }
    
    private abstract static class LocalPool<H, T>
    {
        private final int ratioInterval;
        private final H[] batch;
        private int batchSize;
        private Thread owner;
        private MessagePassingQueue<H> pooledHandles;
        private int ratioCounter;
        
        LocalPool(final int maxCapacity) {
            this.ratioInterval = ((maxCapacity == 0) ? -1 : 0);
            this.owner = null;
            this.batch = null;
            this.batchSize = 0;
            this.pooledHandles = createExternalMcPool(maxCapacity);
            this.ratioCounter = 0;
        }
        
        LocalPool(final Thread owner, final int maxCapacity, final int ratioInterval, final int chunkSize) {
            this.ratioInterval = ratioInterval;
            this.owner = owner;
            this.batch = (H[])((owner != null) ? new Object[chunkSize] : null);
            this.batchSize = 0;
            this.pooledHandles = createExternalScPool(chunkSize, maxCapacity);
            this.ratioCounter = ratioInterval;
        }
        
        private static <H> MessagePassingQueue<H> createExternalMcPool(final int maxCapacity) {
            if (maxCapacity == 0) {
                return null;
            }
            if (Recycler.BLOCKING_POOL) {
                return new BlockingMessageQueue<H>(maxCapacity);
            }
            return (MessagePassingQueue)PlatformDependent.newFixedMpmcQueue(maxCapacity);
        }
        
        private static <H> MessagePassingQueue<H> createExternalScPool(final int chunkSize, final int maxCapacity) {
            if (maxCapacity == 0) {
                return null;
            }
            if (Recycler.BLOCKING_POOL) {
                return new BlockingMessageQueue<H>(maxCapacity);
            }
            return (MessagePassingQueue)PlatformDependent.newMpscQueue(chunkSize, maxCapacity);
        }
        
        LocalPool(final int maxCapacity, final int ratioInterval, final int chunkSize) {
            this((!Recycler.BATCH_FAST_TL_ONLY || FastThreadLocalThread.currentThreadHasFastThreadLocal()) ? Thread.currentThread() : null, maxCapacity, ratioInterval, chunkSize);
        }
        
        protected final H acquire() {
            final int size = this.batchSize;
            if (size != 0) {
                final int top = size - 1;
                final H h = this.batch[top];
                this.batchSize = top;
                this.batch[top] = null;
                return h;
            }
            final MessagePassingQueue<H> handles = this.pooledHandles;
            if (handles == null) {
                return null;
            }
            return handles.relaxedPoll();
        }
        
        protected final void release(final H handle) {
            final Thread owner = this.owner;
            if (owner != null && Thread.currentThread() == owner && this.batchSize < this.batch.length) {
                this.batch[this.batchSize] = handle;
                ++this.batchSize;
            }
            else if (owner != null && isTerminated(owner)) {
                this.pooledHandles = null;
                this.owner = null;
            }
            else {
                final MessagePassingQueue<H> handles = this.pooledHandles;
                if (handles != null) {
                    handles.relaxedOffer(handle);
                }
            }
        }
        
        private static boolean isTerminated(final Thread owner) {
            return PlatformDependent.isJ9Jvm() ? (!owner.isAlive()) : (owner.getState() == Thread.State.TERMINATED);
        }
        
        boolean canAllocatePooled() {
            if (this.ratioInterval < 0) {
                return false;
            }
            if (this.ratioInterval == 0) {
                return true;
            }
            if (++this.ratioCounter >= this.ratioInterval) {
                this.ratioCounter = 0;
                return true;
            }
            return false;
        }
        
        abstract T getWith(final Recycler<T> p0);
        
        int size() {
            final MessagePassingQueue<H> handles = this.pooledHandles;
            final int externalSize = (handles != null) ? handles.size() : 0;
            return externalSize + ((this.batch != null) ? this.batchSize : 0);
        }
    }
    
    private static final class BlockingMessageQueue<T> implements MessagePassingQueue<T>
    {
        private final Queue<T> deque;
        private final int maxCapacity;
        
        BlockingMessageQueue(final int maxCapacity) {
            this.maxCapacity = maxCapacity;
            this.deque = new ArrayDeque<T>();
        }
        
        @Override
        public synchronized boolean offer(final T e) {
            return this.deque.size() != this.maxCapacity && this.deque.offer(e);
        }
        
        @Override
        public synchronized T poll() {
            return this.deque.poll();
        }
        
        @Override
        public synchronized T peek() {
            return this.deque.peek();
        }
        
        @Override
        public synchronized int size() {
            return this.deque.size();
        }
        
        @Override
        public synchronized void clear() {
            this.deque.clear();
        }
        
        @Override
        public synchronized boolean isEmpty() {
            return this.deque.isEmpty();
        }
        
        @Override
        public int capacity() {
            return this.maxCapacity;
        }
        
        @Override
        public boolean relaxedOffer(final T e) {
            return this.offer(e);
        }
        
        @Override
        public T relaxedPoll() {
            return this.poll();
        }
        
        @Override
        public T relaxedPeek() {
            return this.peek();
        }
        
        @Override
        public int drain(final Consumer<T> c, final int limit) {
            int i;
            T obj;
            for (i = 0; i < limit && (obj = this.poll()) != null; ++i) {
                c.accept(obj);
            }
            return i;
        }
        
        @Override
        public int fill(final Supplier<T> s, final int limit) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public int drain(final Consumer<T> c) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public int fill(final Supplier<T> s) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public void drain(final Consumer<T> c, final WaitStrategy wait, final ExitCondition exit) {
            throw new UnsupportedOperationException();
        }
        
        @Override
        public void fill(final Supplier<T> s, final WaitStrategy wait, final ExitCondition exit) {
            throw new UnsupportedOperationException();
        }
    }
    
    public interface Handle<T> extends ObjectPool.Handle<T>
    {
    }
}
