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

package io.netty.util.internal;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import io.netty.util.IllegalReferenceCountException;

public final class RefCnt
{
    private static final int UNSAFE = 0;
    private static final int VAR_HANDLE = 1;
    private static final int ATOMIC_UPDATER = 2;
    private static final int REF_CNT_IMPL;
    volatile int value;
    
    public RefCnt() {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                UnsafeRefCnt.init(this);
                break;
            }
            case 1: {
                VarHandleRefCnt.init(this);
                break;
            }
            default: {
                AtomicRefCnt.init(this);
                break;
            }
        }
    }
    
    public static int refCnt(final RefCnt ref) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                return UnsafeRefCnt.refCnt(ref);
            }
            case 1: {
                return VarHandleRefCnt.refCnt(ref);
            }
            default: {
                return AtomicRefCnt.refCnt(ref);
            }
        }
    }
    
    public static void retain(final RefCnt ref) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                UnsafeRefCnt.retain(ref);
                break;
            }
            case 1: {
                VarHandleRefCnt.retain(ref);
                break;
            }
            default: {
                AtomicRefCnt.retain(ref);
                break;
            }
        }
    }
    
    public static void retain(final RefCnt ref, final int increment) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                UnsafeRefCnt.retain(ref, increment);
                break;
            }
            case 1: {
                VarHandleRefCnt.retain(ref, increment);
                break;
            }
            default: {
                AtomicRefCnt.retain(ref, increment);
                break;
            }
        }
    }
    
    public static boolean release(final RefCnt ref) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                return UnsafeRefCnt.release(ref);
            }
            case 1: {
                return VarHandleRefCnt.release(ref);
            }
            default: {
                return AtomicRefCnt.release(ref);
            }
        }
    }
    
    public static boolean release(final RefCnt ref, final int decrement) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                return UnsafeRefCnt.release(ref, decrement);
            }
            case 1: {
                return VarHandleRefCnt.release(ref, decrement);
            }
            default: {
                return AtomicRefCnt.release(ref, decrement);
            }
        }
    }
    
    public static boolean isLiveNonVolatile(final RefCnt ref) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                return UnsafeRefCnt.isLiveNonVolatile(ref);
            }
            case 1: {
                return VarHandleRefCnt.isLiveNonVolatile(ref);
            }
            default: {
                return AtomicRefCnt.isLiveNonVolatile(ref);
            }
        }
    }
    
    public static void setRefCnt(final RefCnt ref, final int refCnt) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                UnsafeRefCnt.setRefCnt(ref, refCnt);
                break;
            }
            case 1: {
                VarHandleRefCnt.setRefCnt(ref, refCnt);
                break;
            }
            default: {
                AtomicRefCnt.setRefCnt(ref, refCnt);
                break;
            }
        }
    }
    
    public static void resetRefCnt(final RefCnt ref) {
        switch (RefCnt.REF_CNT_IMPL) {
            case 0: {
                UnsafeRefCnt.resetRefCnt(ref);
                break;
            }
            case 1: {
                VarHandleRefCnt.resetRefCnt(ref);
                break;
            }
            default: {
                AtomicRefCnt.resetRefCnt(ref);
                break;
            }
        }
    }
    
    static void throwIllegalRefCountOnRelease(final int decrement, final int curr) {
        throw new IllegalReferenceCountException(curr >>> 1, -(decrement >>> 1));
    }
    
    static {
        if (PlatformDependent.hasUnsafe()) {
            REF_CNT_IMPL = 0;
        }
        else if (PlatformDependent.hasVarHandle()) {
            REF_CNT_IMPL = 1;
        }
        else {
            REF_CNT_IMPL = 2;
        }
    }
    
    private static final class AtomicRefCnt
    {
        private static final AtomicIntegerFieldUpdater<RefCnt> UPDATER;
        
        static void init(final RefCnt instance) {
            AtomicRefCnt.UPDATER.set(instance, 2);
        }
        
        static int refCnt(final RefCnt instance) {
            return AtomicRefCnt.UPDATER.get(instance) >>> 1;
        }
        
        static void retain(final RefCnt instance) {
            retain0(instance, 2);
        }
        
        static void retain(final RefCnt instance, final int increment) {
            retain0(instance, ObjectUtil.checkPositive(increment, "increment") << 1);
        }
        
        private static void retain0(final RefCnt instance, final int increment) {
            final int oldRef = AtomicRefCnt.UPDATER.getAndAdd(instance, increment);
            if ((oldRef & 0x80000001) != 0x0 || oldRef > Integer.MAX_VALUE - increment) {
                AtomicRefCnt.UPDATER.getAndAdd(instance, -increment);
                throw new IllegalReferenceCountException(0, increment >>> 1);
            }
        }
        
        static boolean release(final RefCnt instance) {
            return release0(instance, 2);
        }
        
        static boolean release(final RefCnt instance, final int decrement) {
            return release0(instance, ObjectUtil.checkPositive(decrement, "decrement") << 1);
        }
        
        private static boolean release0(final RefCnt instance, final int decrement) {
            int curr;
            int next;
            do {
                curr = instance.value;
                if (curr == decrement) {
                    next = 1;
                }
                else {
                    if (curr < decrement || (curr & 0x1) == 0x1) {
                        RefCnt.throwIllegalRefCountOnRelease(decrement, curr);
                    }
                    next = curr - decrement;
                }
            } while (!AtomicRefCnt.UPDATER.compareAndSet(instance, curr, next));
            return (next & 0x1) == 0x1;
        }
        
        static void setRefCnt(final RefCnt instance, final int refCnt) {
            final int rawRefCnt = (refCnt > 0) ? (refCnt << 1) : 1;
            AtomicRefCnt.UPDATER.lazySet(instance, rawRefCnt);
        }
        
        static void resetRefCnt(final RefCnt instance) {
            AtomicRefCnt.UPDATER.lazySet(instance, 2);
        }
        
        static boolean isLiveNonVolatile(final RefCnt instance) {
            final int rawCnt = instance.value;
            return rawCnt == 2 || (rawCnt & 0x1) == 0x0;
        }
        
        static {
            UPDATER = AtomicIntegerFieldUpdater.newUpdater(RefCnt.class, "value");
        }
    }
    
    private static final class VarHandleRefCnt
    {
        private static final VarHandle VH;
        
        static void init(final RefCnt instance) {
            VarHandleRefCnt.VH.set(instance, 2);
            VarHandle.storeStoreFence();
        }
        
        static int refCnt(final RefCnt instance) {
            return VarHandleRefCnt.VH.getAcquire(instance) >>> 1;
        }
        
        static void retain(final RefCnt instance) {
            retain0(instance, 2);
        }
        
        static void retain(final RefCnt instance, final int increment) {
            retain0(instance, ObjectUtil.checkPositive(increment, "increment") << 1);
        }
        
        private static void retain0(final RefCnt instance, final int increment) {
            final int oldRef = VarHandleRefCnt.VH.getAndAdd(instance, increment);
            if ((oldRef & 0x80000001) != 0x0 || oldRef > Integer.MAX_VALUE - increment) {
                VarHandleRefCnt.VH.getAndAdd(instance, -increment);
                throw new IllegalReferenceCountException(0, increment >>> 1);
            }
        }
        
        static boolean release(final RefCnt instance) {
            return release0(instance, 2);
        }
        
        static boolean release(final RefCnt instance, final int decrement) {
            return release0(instance, ObjectUtil.checkPositive(decrement, "decrement") << 1);
        }
        
        private static boolean release0(final RefCnt instance, final int decrement) {
            int curr;
            int next;
            do {
                curr = VarHandleRefCnt.VH.get(instance);
                if (curr == decrement) {
                    next = 1;
                }
                else {
                    if (curr < decrement || (curr & 0x1) == 0x1) {
                        RefCnt.throwIllegalRefCountOnRelease(decrement, curr);
                    }
                    next = curr - decrement;
                }
            } while (!VarHandleRefCnt.VH.compareAndSet(instance, curr, next));
            return (next & 0x1) == 0x1;
        }
        
        static void setRefCnt(final RefCnt instance, final int refCnt) {
            final int rawRefCnt = (refCnt > 0) ? (refCnt << 1) : 1;
            VarHandleRefCnt.VH.setRelease(instance, rawRefCnt);
        }
        
        static void resetRefCnt(final RefCnt instance) {
            VarHandleRefCnt.VH.setRelease(instance, 2);
        }
        
        static boolean isLiveNonVolatile(final RefCnt instance) {
            final int rawCnt = VarHandleRefCnt.VH.get(instance);
            return rawCnt == 2 || (rawCnt & 0x1) == 0x0;
        }
        
        static {
            VH = PlatformDependent.findVarHandleOfIntField(MethodHandles.lookup(), RefCnt.class, "value");
        }
    }
    
    private static final class UnsafeRefCnt
    {
        private static final long VALUE_OFFSET;
        
        private static long getUnsafeOffset(final Class<?> clz, final String fieldName) {
            try {
                if (PlatformDependent.hasUnsafe()) {
                    return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
                }
            }
            catch (final Throwable t) {}
            return -1L;
        }
        
        static void init(final RefCnt instance) {
            PlatformDependent.safeConstructPutInt(instance, UnsafeRefCnt.VALUE_OFFSET, 2);
        }
        
        static int refCnt(final RefCnt instance) {
            return PlatformDependent.getVolatileInt(instance, UnsafeRefCnt.VALUE_OFFSET) >>> 1;
        }
        
        static void retain(final RefCnt instance) {
            retain0(instance, 2);
        }
        
        static void retain(final RefCnt instance, final int increment) {
            retain0(instance, ObjectUtil.checkPositive(increment, "increment") << 1);
        }
        
        private static void retain0(final RefCnt instance, final int increment) {
            final int oldRef = PlatformDependent.getAndAddInt(instance, UnsafeRefCnt.VALUE_OFFSET, increment);
            if ((oldRef & 0x80000001) != 0x0 || oldRef > Integer.MAX_VALUE - increment) {
                PlatformDependent.getAndAddInt(instance, UnsafeRefCnt.VALUE_OFFSET, -increment);
                throw new IllegalReferenceCountException(0, increment >>> 1);
            }
        }
        
        static boolean release(final RefCnt instance) {
            return release0(instance, 2);
        }
        
        static boolean release(final RefCnt instance, final int decrement) {
            return release0(instance, ObjectUtil.checkPositive(decrement, "decrement") << 1);
        }
        
        private static boolean release0(final RefCnt instance, final int decrement) {
            int curr;
            int next;
            do {
                curr = PlatformDependent.getInt(instance, UnsafeRefCnt.VALUE_OFFSET);
                if (curr == decrement) {
                    next = 1;
                }
                else {
                    if (curr < decrement || (curr & 0x1) == 0x1) {
                        RefCnt.throwIllegalRefCountOnRelease(decrement, curr);
                    }
                    next = curr - decrement;
                }
            } while (!PlatformDependent.compareAndSwapInt(instance, UnsafeRefCnt.VALUE_OFFSET, curr, next));
            return (next & 0x1) == 0x1;
        }
        
        static void setRefCnt(final RefCnt instance, final int refCnt) {
            final int rawRefCnt = (refCnt > 0) ? (refCnt << 1) : 1;
            PlatformDependent.putOrderedInt(instance, UnsafeRefCnt.VALUE_OFFSET, rawRefCnt);
        }
        
        static void resetRefCnt(final RefCnt instance) {
            PlatformDependent.putOrderedInt(instance, UnsafeRefCnt.VALUE_OFFSET, 2);
        }
        
        static boolean isLiveNonVolatile(final RefCnt instance) {
            final int rawCnt = PlatformDependent.getInt(instance, UnsafeRefCnt.VALUE_OFFSET);
            return rawCnt == 2 || (rawCnt & 0x1) == 0x0;
        }
        
        static {
            VALUE_OFFSET = getUnsafeOffset(RefCnt.class, "value");
        }
    }
}
