Skip to content

Commit

Permalink
WeakIdentityHashMap: Refine capacity computation; Bitfield.Util add '…
Browse files Browse the repository at this point in the history
…PowerOf2' functions
  • Loading branch information
sgothel committed Dec 31, 2019
1 parent c6fabb0 commit 178c7b9
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 2 deletions.
52 changes: 52 additions & 0 deletions src/java/com/jogamp/common/util/Bitfield.java
Expand Up @@ -41,6 +41,14 @@ public interface Bitfield {
* Bit operation utilities (static).
*/
public static class Util {
/**
* Maximum 32bit integer value being of {@link #isPowerOf2(int)}.
* <p>
* We rely on the JVM spec {@link Integer#SIZE} == 32.
* </p>
*/
public static final int MAX_POWER_OF_2 = 1 << ( Integer.SIZE - 2 );

/**
* Returns the 32 bit mask of n-bits, i.e. n low order 1’s.
* <p>
Expand All @@ -67,6 +75,9 @@ public static int getBitMask(final int n) {
* http://tekpool.wordpress.com/category/bit-count/
* http://www.hackersdelight.org/
* </pre>
* <p>
* We rely on the JVM spec {@link Integer#SIZE} == 32.
* </p>
*/
public static final int bitCount(int n) {
// Note: Original used 'unsigned int',
Expand All @@ -87,6 +98,47 @@ public static final int bitCount(int n) {
n = n + (n >>> 16);
return n & 0x3f;
}

/**
* Returns {@code true} if the given integer is a power of 2
* <p>
* Source: bithacks: http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
* </p>
*/
public static final boolean isPowerOf2(final int n) {
return 0<n && 0 == (n & (n - 1));
}
/**
* Returns the next higher power of 2 of 32-bit of given {@code n}
* <p>
* Source: bithacks: http://www.graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
* </p>
* <p>
* We rely on the JVM spec {@link Integer#SIZE} == 32.
* </p>
*/
public static final int nextPowerOf2(int n) {
n--;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : n + 1; // avoid edge case where n is 0, it would return 0, which isn't a power of 2
}
/**
* If the given {@code n} is not {@link #isPowerOf2(int)} return {@link #nextPowerOf2(int)},
* otherwise return {@code n} unchanged.
* <pre>
* return isPowerOf2(n) ? n : nextPowerOf2(n);
* </pre>
* <p>
* We rely on the JVM spec {@link Integer#SIZE} == 32.
* </p>
*/
public static final int roundToPowerOf2(final int n) {
return isPowerOf2(n) ? n : nextPowerOf2(n);
}
}
/**
* Simple {@link Bitfield} factory for returning the efficient implementation.
Expand Down
55 changes: 53 additions & 2 deletions src/java/com/jogamp/common/util/WeakIdentityHashMap.java
Expand Up @@ -89,15 +89,64 @@ public WeakIdentityHashMap() {
* Usable slots before resize are {@code capacity * loadFactor}.
* </p>
* <p>
* Capacity for n-slots w/o resize would be {@code Math.ceil(n/loadFactor)}.
* Capacity for n-slots w/o resize would be {@code (float)n/loadFactor + 1.0f}, see {@link #capacityForRequiredSize(int, float[])}.
* </p>
* @param initialCapacity default value would be 16, i.e. 12 slots @ 0.75f loadFactor before resize
* @param loadFactor default value would be 0.75f
* @see #capacityForRequiredSize(int, float[])
* @see #createWithRequiredSize(int, float)
*/
public WeakIdentityHashMap(final int initialCapacity, final float loadFactor) {
backingStore = new HashMap<>(initialCapacity, loadFactor);
}

/**
* Static creation method using {@link #capacityForRequiredSize(int, float[])}
* to instantiate a new {@link WeakIdentityHashMap} via {@link #WeakIdentityHashMap(int, float)}.
*
* @param requiredSize the user desired n-slots before resize
* @param loadFactor given loadFactor, which might be increased a little to avoid next PowerOf2 bloat
* @return the new {@link WeakIdentityHashMap} instance
*/
@SuppressWarnings("rawtypes")
public static WeakIdentityHashMap<?, ?> createWithRequiredSize(final int requiredSize, final float loadFactor) {
final float[] lf = { loadFactor };
final int icap = capacityForRequiredSize(requiredSize, lf);
return new WeakIdentityHashMap(icap, lf[0]);
}

/**
* Returns the [initial] capacity using the given {@code loadFactor}
* and {@code requiredSize}.
* <p>
* General calculation is {@code (float)requiredSize/loadFactor + 1.0f}, using {@code loadFactor := 0.75f}.
* </p>
* <p>
* In case above computed capacity is {@link Bitfield.Util#isPowerOf2(int)},
* the given {@code loadFactor} will be increased to avoid next PowerOf2 table size initialization.
* </p>
* @param requiredSize the user desired n-slots before resize
* @param loadFactor given loadFactor, which might be increased a little to avoid next PowerOf2 bloat
* @return the [initial] capacity to be used for {@link #WeakIdentityHashMap(int, float)}
*/
public static int capacityForRequiredSize(final int requiredSize, final float[] loadFactor) {
if( requiredSize >= Bitfield.Util.MAX_POWER_OF_2 ) {
return Integer.MAX_VALUE;
}
float lf = loadFactor[0];
int c0 = (int)( requiredSize/lf + 1.0f );
if( !Bitfield.Util.isPowerOf2(c0) || 0.86f <= lf ) {
return c0;
}
do {
lf += 0.01f;
c0 = (int)( requiredSize/lf + 1.0f );
} while( Bitfield.Util.isPowerOf2(c0) && 0.86f > lf );

loadFactor[0] = lf;
return c0;
}

@Override
public void clear() {
backingStore.clear();
Expand Down Expand Up @@ -192,7 +241,9 @@ public boolean isEmpty() {
public void putAll(final Map<? extends K, ? extends V> t) {
final int n = t.size();
if ( 0 < n ) {
final Map<IdentityWeakReference<K>, V> t2 = new HashMap<>((int)Math.ceil(n/0.75), 0.75f);
final float[] lf = { 0.75f };
final int icap = capacityForRequiredSize(n, lf);
final Map<IdentityWeakReference<K>, V> t2 = new HashMap<>(icap, lf[0]);
for (final Map.Entry<? extends K, ? extends V> e : t.entrySet()) {
t2.put(new IdentityWeakReference<K>(e.getKey(), queue), e.getValue());
}
Expand Down

0 comments on commit 178c7b9

Please sign in to comment.