package water;
import water.fvec.Chunk;
import water.fvec.ChunkUtils;
import water.fvec.Frame;
import water.parser.BufferedString;
import java.io.IOException;
import java.nio.channels.ByteChannel;
import java.util.UUID;
import static water.ExternalFrameUtils.*;
/**
* This class contains methods used on the h2o backend to read data from H2O Frame.
*/
final class ExternalFrameReaderBackend {
/**
* Internal method use on the h2o backend side to handle reading from the chunk from non-h2o environment
* @param channel socket channel originating from non-h2o node
* @param initAb {@link AutoBuffer} containing information necessary for preparing backend for reading
*/
static void handleReadingFromChunk(ByteChannel channel, AutoBuffer initAb) throws IOException {
// receive required information
String frameKey = initAb.getStr();
int chunkIdx = initAb.getInt();
byte[] expectedTypes = initAb.getA1();
assert expectedTypes != null : "Expected types can't be null";
int[] selectedColumnIndices = initAb.getA4();
assert selectedColumnIndices != null : "Selected column indices can't be null";
Frame fr = DKV.getGet(frameKey);
Chunk[] chunks = ChunkUtils.getChunks(fr, chunkIdx);
// write number of rows
AutoBuffer ab = new AutoBuffer();
ab.putInt(chunks[0]._len);
writeToChannel(ab, channel);
// buffered string to be reused for strings to avoid multiple allocation in the loop
BufferedString valStr = new BufferedString();
for (int rowIdx = 0; rowIdx < chunks[0]._len; rowIdx++) {
for(int i = 0; i < selectedColumnIndices.length; i++){
if (chunks[selectedColumnIndices[i]].isNA(rowIdx)) {
ExternalFrameUtils.sendNA(ab, channel, expectedTypes[i]);
} else {
final Chunk chnk = chunks[selectedColumnIndices[i]];
switch (expectedTypes[i]) {
case EXPECTED_BOOL:
ExternalFrameUtils.sendBoolean(ab, channel, (byte)chnk.at8(rowIdx));
break;
case EXPECTED_BYTE:
ExternalFrameUtils.sendByte(ab, channel, (byte)chnk.at8(rowIdx));
break;
case EXPECTED_CHAR:
ExternalFrameUtils.sendChar(ab, channel, (char)chnk.at8(rowIdx));
break;
case EXPECTED_SHORT:
ExternalFrameUtils.sendShort(ab, channel, (short)chnk.at8(rowIdx));
break;
case EXPECTED_INT:
ExternalFrameUtils.sendInt(ab, channel, (int)chnk.at8(rowIdx));
break;
case EXPECTED_FLOAT:
ExternalFrameUtils.sendFloat(ab, channel, (float)chnk.atd(rowIdx));
break;
case EXPECTED_LONG:
ExternalFrameUtils.sendLong(ab, channel, chnk.at8(rowIdx));
break;
case EXPECTED_DOUBLE:
ExternalFrameUtils.sendDouble(ab, channel, chnk.atd(rowIdx));
break;
case EXPECTED_TIMESTAMP:
ExternalFrameUtils.sendTimestamp(ab, channel, chnk.at8(rowIdx));
break;
case EXPECTED_STRING:
if (chnk.vec().isCategorical()) {
ExternalFrameUtils.sendString(ab, channel, chnk.vec().domain()[(int) chnk.at8(rowIdx)]);
} else if (chnk.vec().isString()) {
ExternalFrameUtils.sendString(ab, channel, chnk.atStr(valStr, rowIdx).toString());
} else if (chnk.vec().isUUID()) {
UUID uuid = new UUID(chnk.at16h(rowIdx), chnk.at16l(rowIdx));
ExternalFrameUtils.sendString(ab, channel, uuid.toString());
} else {
assert false : "Can never be here";
}
break;
}
}
}
}
ab.put1(ExternalFrameHandler.CONFIRM_READING_DONE);
writeToChannel(ab, channel);
}
}