package water;
import java.util.Arrays;
import water.nbhm.NonBlockingHashMap;
import water.util.Log;
import water.H2O;
public class TypeMap {
static public final short NULL = (short) -1;
static public final short PRIM_B = 1;
static public final short C1NCHUNK;
static public final short FRAME;
static final public String BOOTSTRAP_CLASSES[] = {
" BAD",
"[B",
"water.FetchClazz", // used to fetch IDs from leader
"water.FetchId", // used to fetch IDs from leader
"water.ValueArray", // used in TypeaheadKeys
"water.fvec.C1NChunk",// used as constant in parser
"water.fvec.Frame", // used in TypeaheadKeys & Exec2
"water.TaskPutKey", // Needed to write that first Key
"water.Key", // Needed to write that first Key
"water.Value", // Needed to write that first Key
"water.TaskGetKey", // Read that first Key
"water.Job$List", // First Key which locks the cloud for all JUnit tests
"water.DException", // Do not fetch clazz during distributed exception reporting
"water.UniqueFrameId",// Do not fetch clazz during distributed exception reporting
};
// String -> ID mapping
static private final NonBlockingHashMap<String, Integer> MAP = new NonBlockingHashMap();
// ID -> String mapping
static private String[] CLAZZES;
// ID -> pre-allocated Golden Instance of class
static private Freezable[] GOLD;
// Unique ides
static private int IDS;
static {
CLAZZES = BOOTSTRAP_CLASSES;
GOLD = new Freezable[BOOTSTRAP_CLASSES.length];
int id=0;
for( String s : CLAZZES )
MAP.put(s,id++);
IDS = id;
C1NCHUNK = (short)onIce("water.fvec.C1NChunk");
FRAME = (short)onIce("water.fvec.Frame");
}
// During first Icing, get a globally unique class ID for a className
static public int onIce(String className) {
Integer I = MAP.get(className);
if( I != null ) return I;
// Need to install a new cloud-wide type ID for className
assert H2O.CLOUD.size() > 0 : "No cloud when getting type id for "+className;
int id = -1;
if( H2O.CLOUD.leader() != H2O.SELF ) // Not leader?
id = FetchId.fetchId(className);
return install(className,id);
}
// Install the type mapping under lock, and grow all the arrays as needed.
// The grow-step is not obviously race-safe: readers of all the arrays will
// get either the old or new arrays. However readers are all reader with
// smaller type ids, and these will work fine in either old or new arrays.
synchronized static private int install( String className, int id ) {
Paxos.lockCloud();
if( id == -1 ) id = IDS++; // Leader will get an ID under lock
MAP.put(className,id); // No race on insert, since under lock
// Expand lists to handle new ID, as needed
if( id >= CLAZZES.length ) CLAZZES = Arrays.copyOf(CLAZZES,Math.max(CLAZZES.length<<1,id+1));
if( id >= GOLD .length ) GOLD = Arrays.copyOf(GOLD ,Math.max(CLAZZES.length<<1,id+1));
CLAZZES[id] = className;
return id;
}
// During deserialization, figure out the mapping from a type ID to a type
// String (and Class). Mostly forced into another class to avoid circular
// class-loading issues.
static public void loadId(int id) {
assert H2O.CLOUD.leader() != H2O.SELF; // Leaders always have the latest mapping already
install( FetchClazz.fetchClazz(id), id );
}
static public Iced newInstance(int id) {
if( id >= CLAZZES.length || CLAZZES[id] == null ) loadId(id);
Iced f = (Iced) GOLD[id];
if( f == null ) {
try { GOLD[id] = f = (Iced) Class.forName(CLAZZES[id]).newInstance(); }
catch( Exception e ) { Log.err("Failed newinstance for class "+className(id)); throw Log.errRTExcept(e); }
}
return f.newInstance();
}
static public Freezable newFreezable(int id) {
assert id >= 0 : "Bad type id "+id;
if( id >= CLAZZES.length || CLAZZES[id] == null ) loadId(id);
Freezable f = GOLD[id];
if( f == null ) {
try { GOLD[id] = f = (Freezable) Class.forName(CLAZZES[id]).newInstance(); }
catch( Exception e ) {
throw Log.errRTExcept(e);
}
}
return f.newInstance();
}
static public String className(int id) {
if( id >= CLAZZES.length || CLAZZES[id] == null ) loadId(id);
assert CLAZZES[id] != null : "No class matching id "+id;
return CLAZZES[id];
}
static public Class clazz(int id) {
if( id >= CLAZZES.length || CLAZZES[id] == null ) loadId(id);
if( GOLD[id] == null ) newInstance(id);
return GOLD[id].getClass();
}
}