/*
 * Copyright (C) 2011-2014 Chris Vest (mr.chrisvest@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package stormpot;

import java.lang.ref.WeakReference;

final class PreciseLeakDetector {
  private static class WeakRef extends WeakReference<Object> {
    WeakRef next;

    public WeakRef(Object referent) {
      super(referent);
    }
  }

  // Both guarded by synchronized(this)
  private Object[] probes;
  private int leakedObjectCount;

  PreciseLeakDetector() {
    // The probes are basically a two-level Bagwell hash trie with linked list
    // buckets at the end, to reduce the proliferation of arrays.
    // Basically, we compute the identity hash code for an object, and the
    // least significant 5 bits are the index into the first-level 32 element
    // 'probe' array. When inserting, if that slot is null, then we insert the
    // WeakRef directly into that place. In fact, we allow building a WeakRef
    // chain up to 4 elements long, directly in the first-level 'probes' array.
    // Otherwise we add the second-level 32 element array, rehash the existing
    // chain into it, by using the next 5 second-least significant bits as
    // index. From here on, we don't create any more levels, but just link the
    // WeakRefs together like a daisy-chain. This way, we create at most 33
    // arrays, with room for 1024 objects. Most pools probably won't need to
    // have more than that many objects registered at a time.
    probes = new Object[32];
  }

  synchronized void register(Object obj) {
    int hash1 = System.identityHashCode(obj) & 31;

    Object current = probes[hash1];

    if (current == null) {
      probes[hash1] = new WeakRef(obj);
      return;
    }

    if (current instanceof WeakRef) {
      WeakRef currentRef = (WeakRef) current;
      if (chainShorterThan(4, current)) {
        WeakRef ref = new WeakRef(obj);
        ref.next = currentRef;
        probes[hash1] = ref;
      } else {
        WeakRef[] level2 = new WeakRef[32];
        WeakRef ref = new WeakRef(obj);
        ref.next = currentRef;
        assocLevel2(level2, ref);
        probes[hash1] = level2;
      }
      return;
    }

    WeakRef[] level2 = (WeakRef[]) current;
    assocLevel2(level2, new WeakRef(obj));
  }

  private void assocLevel2(WeakRef[] level2, WeakRef chain) {
    WeakRef next;
    Object obj;
    while (chain != null) {
      next = chain.next;
      obj = chain.get();

      if (obj != null) {
        int hash2 = (System.identityHashCode(obj) >> 5) & 31;
        chain.next = level2[hash2];
        level2[hash2] = chain;
      } else {
        leakedObjectCount++;
      }

      chain = next;
    }
  }

  private boolean chainShorterThan(int length, Object weakRef) {
    WeakRef ref = (WeakRef) weakRef;

    while (ref != null) {
      if (length == 0) {
        return false;
      }
      length--;
      ref = ref.next;
    }
    return true;
  }

  synchronized void unregister(Object obj) {
    int hash0 = System.identityHashCode(obj);
    int hash1 = hash0 & 31;

    Object current = probes[hash1];

    if (current instanceof WeakRef) {
      WeakRef ref = (WeakRef) current;
      probes[hash1] = removeFromChain(ref, obj);
      return;
    }

    int hash2 = (hash0 >> 5) & 31;
    WeakRef[] level2 = (WeakRef[]) current;
    level2[hash2] = removeFromChain(level2[hash2], obj);
  }

  private WeakRef removeFromChain(WeakRef chain, Object obj) {
    WeakRef newChain = null;
    while (chain != null) {
      WeakRef next = chain.next;
      Object current = chain.get();
      if (current == null) {
        leakedObjectCount++;
      } else if (current == obj) {
        chain.clear();
      } else {
        chain.next = newChain;
        newChain = chain;
      }
      chain = next;
    }
    return newChain;
  }

  synchronized long countLeakedObjects() {
    for (int i = 0; i < probes.length; i++) {
      Object current = probes[i];
      if (current instanceof WeakRef) {
        probes[i] = pruneChain((WeakRef) current);
      } else if (current instanceof WeakRef[]) {
        WeakRef[] level2 = (WeakRef[]) current;
        for (int j = 0; j < level2.length; j++) {
          level2[j] = pruneChain(level2[j]);
        }
      }
    }

    return leakedObjectCount;
  }

  private WeakRef pruneChain(WeakRef chain) {
    WeakRef newChain = null;
    while (chain != null) {
      WeakRef next = chain.next;
      if (chain.get() == null) {
        leakedObjectCount++;
      } else {
        chain.next = newChain;
        newChain = chain;
      }
      chain = next;
    }
    return newChain;
  }
}

@@ -715,16 +715,249 @@
 ector %7B%0A
+  private static final int branchPower =%0A      Integer.getInteger(%22stormpot.PreciseLeakDetector.branchPower%22, 5);%0A  private static final int branchFactor = 1 %3C%3C branchPower;%0A  private static final int branchMask = branchFactor - 1;%0A%0A
   privat
@@ -1011,16 +1011,16 @@
 ject%3E %7B%0A
-
     Weak
@@ -2236,18 +2236,28 @@
  Object%5B
-32
+branchFactor
 %5D;%0A  %7D%0A%0A
@@ -2346,18 +2346,26 @@
 (obj) & 
-31
+branchMask
 ;%0A%0A    O
@@ -2764,18 +2764,28 @@
 WeakRef%5B
-32
+branchFactor
 %5D;%0A     
@@ -3286,31 +3286,49 @@
 ode(obj) %3E%3E 
-5) & 31
+branchPower) & branchMask
 ;%0A        ch
@@ -3833,18 +3833,26 @@
 hash0 & 
-31
+branchMask
 ;%0A%0A    O
@@ -4060,15 +4060,33 @@
  %3E%3E 
-5) & 31
+branchPower) & branchMask
 ;%0A  

Add hidden configuration for the branching power of the Bagwell map backing the PreciseLeakDetector