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

package io.netty.buffer;

import java.util.concurrent.locks.ReentrantLock;

final class PoolSubpage<T> implements PoolSubpageMetric
{
    final PoolChunk<T> chunk;
    final int elemSize;
    private final int pageShifts;
    private final int runOffset;
    private final int runSize;
    private final long[] bitmap;
    private final int bitmapLength;
    private final int maxNumElems;
    final int headIndex;
    PoolSubpage<T> prev;
    PoolSubpage<T> next;
    boolean doNotDestroy;
    private int nextAvail;
    private int numAvail;
    final ReentrantLock lock;
    
    PoolSubpage(final int headIndex) {
        this.chunk = null;
        this.lock = new ReentrantLock();
        this.pageShifts = -1;
        this.runOffset = -1;
        this.elemSize = -1;
        this.runSize = -1;
        this.bitmap = null;
        this.bitmapLength = -1;
        this.maxNumElems = 0;
        this.headIndex = headIndex;
    }
    
    PoolSubpage(final PoolSubpage<T> head, final PoolChunk<T> chunk, final int pageShifts, final int runOffset, final int runSize, final int elemSize) {
        this.headIndex = head.headIndex;
        this.chunk = chunk;
        this.pageShifts = pageShifts;
        this.runOffset = runOffset;
        this.runSize = runSize;
        this.elemSize = elemSize;
        this.doNotDestroy = true;
        final int n = runSize / elemSize;
        this.numAvail = n;
        this.maxNumElems = n;
        int bitmapLength = this.maxNumElems >>> 6;
        if ((this.maxNumElems & 0x3F) != 0x0) {
            ++bitmapLength;
        }
        this.bitmapLength = bitmapLength;
        this.bitmap = new long[bitmapLength];
        this.nextAvail = 0;
        this.lock = null;
        this.addToPool(head);
    }
    
    long allocate() {
        if (this.numAvail == 0 || !this.doNotDestroy) {
            return -1L;
        }
        final int bitmapIdx = this.getNextAvail();
        if (bitmapIdx < 0) {
            this.removeFromPool();
            throw new AssertionError((Object)("No next available bitmap index found (bitmapIdx = " + bitmapIdx + "), even though there are supposed to be (numAvail = " + this.numAvail + ") out of (maxNumElems = " + this.maxNumElems + ") available indexes."));
        }
        final int q = bitmapIdx >>> 6;
        final int r = bitmapIdx & 0x3F;
        assert (this.bitmap[q] >>> r & 0x1L) == 0x0L;
        final long[] bitmap = this.bitmap;
        final int n = q;
        bitmap[n] |= 1L << r;
        if (--this.numAvail == 0) {
            this.removeFromPool();
        }
        return this.toHandle(bitmapIdx);
    }
    
    boolean free(final PoolSubpage<T> head, final int bitmapIdx) {
        final int q = bitmapIdx >>> 6;
        final int r = bitmapIdx & 0x3F;
        assert (this.bitmap[q] >>> r & 0x1L) != 0x0L;
        final long[] bitmap = this.bitmap;
        final int n = q;
        bitmap[n] ^= 1L << r;
        this.setNextAvail(bitmapIdx);
        if (this.numAvail++ == 0) {
            this.addToPool(head);
            if (this.maxNumElems > 1) {
                return true;
            }
        }
        if (this.numAvail != this.maxNumElems) {
            return true;
        }
        if (this.prev == this.next) {
            return true;
        }
        this.doNotDestroy = false;
        this.removeFromPool();
        return false;
    }
    
    private void addToPool(final PoolSubpage<T> head) {
        assert this.prev == null && this.next == null;
        this.prev = head;
        this.next = head.next;
        this.next.prev = this;
        head.next = this;
    }
    
    private void removeFromPool() {
        assert this.prev != null && this.next != null;
        this.prev.next = this.next;
        this.next.prev = this.prev;
        this.next = null;
        this.prev = null;
    }
    
    private void setNextAvail(final int bitmapIdx) {
        this.nextAvail = bitmapIdx;
    }
    
    private int getNextAvail() {
        final int nextAvail = this.nextAvail;
        if (nextAvail >= 0) {
            this.nextAvail = -1;
            return nextAvail;
        }
        return this.findNextAvail();
    }
    
    private int findNextAvail() {
        for (int i = 0; i < this.bitmapLength; ++i) {
            final long bits = this.bitmap[i];
            if (~bits != 0x0L) {
                return this.findNextAvail0(i, bits);
            }
        }
        return -1;
    }
    
    private int findNextAvail0(final int i, long bits) {
        final int baseVal = i << 6;
        int j = 0;
        while (j < 64) {
            if ((bits & 0x1L) == 0x0L) {
                final int val = baseVal | j;
                if (val < this.maxNumElems) {
                    return val;
                }
                break;
            }
            else {
                bits >>>= 1;
                ++j;
            }
        }
        return -1;
    }
    
    private long toHandle(final int bitmapIdx) {
        final int pages = this.runSize >> this.pageShifts;
        return (long)this.runOffset << 49 | (long)pages << 34 | 0x200000000L | 0x100000000L | (long)bitmapIdx;
    }
    
    @Override
    public String toString() {
        int numAvail;
        if (this.chunk == null) {
            numAvail = 0;
        }
        else {
            final PoolSubpage<T> head = this.chunk.arena.smallSubpagePools[this.headIndex];
            head.lock();
            boolean doNotDestroy;
            try {
                doNotDestroy = this.doNotDestroy;
                numAvail = this.numAvail;
            }
            finally {
                head.unlock();
            }
            if (!doNotDestroy) {
                return "(" + this.runOffset + ": not in use)";
            }
        }
        return "(" + this.runOffset + ": " + (this.maxNumElems - numAvail) + '/' + this.maxNumElems + ", offset: " + this.runOffset + ", length: " + this.runSize + ", elemSize: " + this.elemSize + ')';
    }
    
    @Override
    public int maxNumElements() {
        return this.maxNumElems;
    }
    
    @Override
    public int numAvailable() {
        if (this.chunk == null) {
            return 0;
        }
        final PoolSubpage<T> head = this.chunk.arena.smallSubpagePools[this.headIndex];
        head.lock();
        try {
            return this.numAvail;
        }
        finally {
            head.unlock();
        }
    }
    
    @Override
    public int elementSize() {
        return this.elemSize;
    }
    
    @Override
    public int pageSize() {
        return 1 << this.pageShifts;
    }
    
    boolean isDoNotDestroy() {
        if (this.chunk == null) {
            return true;
        }
        final PoolSubpage<T> head = this.chunk.arena.smallSubpagePools[this.headIndex];
        head.lock();
        try {
            return this.doNotDestroy;
        }
        finally {
            head.unlock();
        }
    }
    
    void destroy() {
        if (this.chunk != null) {
            this.chunk.destroy();
        }
    }
    
    void lock() {
        this.lock.lock();
    }
    
    void unlock() {
        this.lock.unlock();
    }
}
