package org.jruby;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
This class serves as a registry of all bit flags we use on JRuby objects.
In order to maximally use our bit flags and prevent overlap between ancestors and dependents,
this class registers flags on a first-come, first-served basis using previous flags registered
for ancestor classes as a base line for new flags in a descendant.
Because of the first-come, first-served nature, the most general types will need to register
their flags first. This guarantees all bit flags from the progenitor on down will be packed
tightly while avoiding overlaps.
/**
* This class serves as a registry of all bit flags we use on JRuby objects.
*
* In order to maximally use our bit flags and prevent overlap between ancestors and dependents,
* this class registers flags on a first-come, first-served basis using previous flags registered
* for ancestor classes as a base line for new flags in a descendant.
*
* Because of the first-come, first-served nature, the most general types will need to register
* their flags first. This guarantees all bit flags from the progenitor on down will be packed
* tightly while avoiding overlaps.
*/
public class FlagRegistry {
private final Map<Class, Integer> currentShift = new HashMap<>();
private final Map<Class, BitSet> registry = new HashMap<>();
Register a new flag for the given class.
The bit index for the new flag will be calculated at runtime, by walking parent classes
and looking for previously-registered flags. Ancestors should register all their flags
before descendants (which means they should not be registered in a static initializer
unless the parent is known to have fully run its own static initializers).
Params: - klass – the class for which to register a new flag
Returns: an integer with the new flag bit set
/**
* Register a new flag for the given class.
*
* The bit index for the new flag will be calculated at runtime, by walking parent classes
* and looking for previously-registered flags. Ancestors should register all their flags
* before descendants (which means they should not be registered in a static initializer
* unless the parent is known to have fully run its own static initializers).
*
* @param klass the class for which to register a new flag
* @return an integer with the new flag bit set
*/
public synchronized int newFlag(Class klass) {
Class currentKlass = klass;
Integer shift = null;
while (currentKlass != null &&
(shift = currentShift.get(currentKlass)) == null) {
currentKlass = currentKlass.getSuperclass();
}
if (shift == null) shift = 0;
BitSet flags = registry.get(klass);
if (flags == null) {
flags = new BitSet();
registry.put(klass, flags);
}
flags.set(shift);
assert flagsAreValid(klass, shift);
currentShift.put(klass, shift + 1);
return 1 << shift++;
}
public synchronized void printFlags() {
System.out.println(registry);
}
private boolean flagsAreValid(Class klass, int bitIndex) {
BitSet gathered = new BitSet();
Class currentKlass = klass;
while (currentKlass != null) {
BitSet flags = registry.get(klass);
if (flags != null) {
if (flags.intersects(gathered)) {
throw new AssertionError(klass.getName() + " uses flag " + bitIndex + " that overlaps with " + currentKlass);
}
gathered.and(flags);
}
currentKlass = currentKlass.getSuperclass();
}
return true;
}
}