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

package io.netty.util.concurrent;

import io.netty.util.internal.ObjectUtil;
import java.util.Objects;
import io.netty.util.internal.MathUtil;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.IntSupplier;
import java.util.function.IntConsumer;

public interface MpscIntQueue
{
    default MpscIntQueue create(final int size, final int emptyValue) {
        return new MpscAtomicIntegerArrayQueue(size, emptyValue);
    }
    
    boolean offer(final int p0);
    
    int poll();
    
    int drain(final int p0, final IntConsumer p1);
    
    int fill(final int p0, final IntSupplier p1);
    
    boolean isEmpty();
    
    int size();
    
    public static final class MpscAtomicIntegerArrayQueue extends AtomicIntegerArray implements MpscIntQueue
    {
        private static final long serialVersionUID = 8740338425124821455L;
        private static final AtomicLongFieldUpdater<MpscAtomicIntegerArrayQueue> PRODUCER_INDEX;
        private static final AtomicLongFieldUpdater<MpscAtomicIntegerArrayQueue> PRODUCER_LIMIT;
        private static final AtomicLongFieldUpdater<MpscAtomicIntegerArrayQueue> CONSUMER_INDEX;
        private final int mask;
        private final int emptyValue;
        private volatile long producerIndex;
        private volatile long producerLimit;
        private volatile long consumerIndex;
        
        public MpscAtomicIntegerArrayQueue(final int capacity, final int emptyValue) {
            super(MathUtil.safeFindNextPositivePowerOfTwo(capacity));
            if (emptyValue != 0) {
                this.emptyValue = emptyValue;
                final int end = this.length() - 1;
                for (int i = 0; i < end; ++i) {
                    this.lazySet(i, emptyValue);
                }
                this.getAndSet(end, emptyValue);
            }
            else {
                this.emptyValue = 0;
            }
            this.mask = this.length() - 1;
        }
        
        @Override
        public boolean offer(final int value) {
            if (value == this.emptyValue) {
                throw new IllegalArgumentException("Cannot offer the \"empty\" value: " + this.emptyValue);
            }
            final int mask = this.mask;
            long producerLimit = this.producerLimit;
            long pIndex;
            do {
                pIndex = this.producerIndex;
                if (pIndex >= producerLimit) {
                    final long cIndex = this.consumerIndex;
                    producerLimit = cIndex + mask + 1L;
                    if (pIndex >= producerLimit) {
                        return false;
                    }
                    MpscAtomicIntegerArrayQueue.PRODUCER_LIMIT.lazySet(this, producerLimit);
                }
            } while (!MpscAtomicIntegerArrayQueue.PRODUCER_INDEX.compareAndSet(this, pIndex, pIndex + 1L));
            final int offset = (int)(pIndex & (long)mask);
            this.lazySet(offset, value);
            return true;
        }
        
        @Override
        public int poll() {
            final long cIndex = this.consumerIndex;
            final int offset = (int)(cIndex & (long)this.mask);
            int value = this.get(offset);
            if (this.emptyValue == value) {
                if (cIndex == this.producerIndex) {
                    return this.emptyValue;
                }
                do {
                    value = this.get(offset);
                } while (this.emptyValue == value);
            }
            this.lazySet(offset, this.emptyValue);
            MpscAtomicIntegerArrayQueue.CONSUMER_INDEX.lazySet(this, cIndex + 1L);
            return value;
        }
        
        @Override
        public int drain(final int limit, final IntConsumer consumer) {
            Objects.requireNonNull(consumer, "consumer");
            ObjectUtil.checkPositiveOrZero(limit, "limit");
            if (limit == 0) {
                return 0;
            }
            final int mask = this.mask;
            final long cIndex = this.consumerIndex;
            for (int i = 0; i < limit; ++i) {
                final long index = cIndex + i;
                final int offset = (int)(index & (long)mask);
                final int value = this.get(offset);
                if (this.emptyValue == value) {
                    return i;
                }
                this.lazySet(offset, this.emptyValue);
                MpscAtomicIntegerArrayQueue.CONSUMER_INDEX.lazySet(this, index + 1L);
                consumer.accept(value);
            }
            return limit;
        }
        
        @Override
        public int fill(final int limit, final IntSupplier supplier) {
            Objects.requireNonNull(supplier, "supplier");
            ObjectUtil.checkPositiveOrZero(limit, "limit");
            if (limit == 0) {
                return 0;
            }
            final int mask = this.mask;
            final long capacity = mask + 1;
            long producerLimit = this.producerLimit;
            long pIndex;
            int actualLimit;
            do {
                pIndex = this.producerIndex;
                long available = producerLimit - pIndex;
                if (available <= 0L) {
                    final long cIndex = this.consumerIndex;
                    producerLimit = cIndex + capacity;
                    available = producerLimit - pIndex;
                    if (available <= 0L) {
                        return 0;
                    }
                    MpscAtomicIntegerArrayQueue.PRODUCER_LIMIT.lazySet(this, producerLimit);
                }
                actualLimit = Math.min((int)available, limit);
            } while (!MpscAtomicIntegerArrayQueue.PRODUCER_INDEX.compareAndSet(this, pIndex, pIndex + actualLimit));
            for (int i = 0; i < actualLimit; ++i) {
                final int offset = (int)(pIndex + i & (long)mask);
                this.lazySet(offset, supplier.getAsInt());
            }
            return actualLimit;
        }
        
        @Override
        public boolean isEmpty() {
            final long cIndex = this.consumerIndex;
            final long pIndex = this.producerIndex;
            return cIndex >= pIndex;
        }
        
        @Override
        public int size() {
            long after = this.consumerIndex;
            long before;
            long pIndex;
            do {
                before = after;
                pIndex = this.producerIndex;
                after = this.consumerIndex;
            } while (before != after);
            final long size = pIndex - after;
            return (size < 0L) ? 0 : ((size > 2147483647L) ? Integer.MAX_VALUE : ((int)size));
        }
        
        static {
            PRODUCER_INDEX = AtomicLongFieldUpdater.newUpdater(MpscAtomicIntegerArrayQueue.class, "producerIndex");
            PRODUCER_LIMIT = AtomicLongFieldUpdater.newUpdater(MpscAtomicIntegerArrayQueue.class, "producerLimit");
            CONSUMER_INDEX = AtomicLongFieldUpdater.newUpdater(MpscAtomicIntegerArrayQueue.class, "consumerIndex");
        }
    }
}
