package org.embulk.spi; import io.airlift.slice.Slice; import io.airlift.slice.Slices; import org.msgpack.value.Value; import org.embulk.spi.time.Timestamp; public class PageReader implements AutoCloseable { private final Schema schema; private final int[] columnOffsets; private Page page = SENTINEL; private Slice pageSlice = null; private int pageRecordCount = 0; private int readCount = 0; private int position; private final byte[] nullBitSet; private static final Page SENTINEL = Page.wrap(Buffer.wrap(new byte[4])); // buffer().release() does nothing public PageReader(Schema schema) { this.schema = schema; this.columnOffsets = PageFormat.columnOffsets(schema); this.nullBitSet = new byte[PageFormat.nullBitSetSize(schema)]; } public static int getRecordCount(Page page) { Buffer pageBuffer = page.buffer(); Slice pageSlice = Slices.wrappedBuffer(pageBuffer.array(), pageBuffer.offset(), pageBuffer.limit()); return pageSlice.getInt(0); // see page format } public void setPage(Page page) { this.page.buffer().release(); this.page = SENTINEL; Buffer pageBuffer = page.buffer(); Slice pageSlice = Slices.wrappedBuffer(pageBuffer.array(), pageBuffer.offset(), pageBuffer.limit()); pageRecordCount = pageSlice.getInt(0); // see page format readCount = 0; position = PageFormat.PAGE_HEADER_SIZE; this.page = page; this.pageSlice = pageSlice; } public Schema getSchema() { return schema; } public boolean isNull(Column column) { return isNull(column.getIndex()); } public boolean isNull(int columnIndex) { return (nullBitSet[columnIndex >>> 3] & (1 << (columnIndex & 7))) != 0; } public boolean getBoolean(Column column) { // TODO check type? return getBoolean(column.getIndex()); } public boolean getBoolean(int columnIndex) { return pageSlice.getByte(getOffset(columnIndex)) != (byte) 0; } public long getLong(Column column) { // TODO check type? return getLong(column.getIndex()); } public long getLong(int columnIndex) { return pageSlice.getLong(getOffset(columnIndex)); } public double getDouble(Column column) { // TODO check type? return getDouble(column.getIndex()); } public double getDouble(int columnIndex) { return pageSlice.getDouble(getOffset(columnIndex)); } public String getString(Column column) { // TODO check type? return getString(column.getIndex()); } public String getString(int columnIndex) { int index = pageSlice.getInt(getOffset(columnIndex)); return page.getStringReference(index); } public Timestamp getTimestamp(Column column) { // TODO check type? return getTimestamp(column.getIndex()); } public Timestamp getTimestamp(int columnIndex) { int offset = getOffset(columnIndex); long sec = pageSlice.getLong(offset); int nsec = pageSlice.getInt(offset + 8); return Timestamp.ofEpochSecond(sec, nsec); } public Value getJson(Column column) { // TODO check type? return getJson(column.getIndex()); } public Value getJson(int columnIndex) { int index = pageSlice.getInt(getOffset(columnIndex)); return page.getValueReference(index); } private int getOffset(int columnIndex) { return position + columnOffsets[columnIndex]; } public boolean nextRecord() { if (pageRecordCount <= readCount) { return false; } if (readCount > 0) { // advance position excepting the first record int lastRecordSize = pageSlice.getInt(position); position += lastRecordSize; } readCount++; pageSlice.getBytes(position + 4, nullBitSet, 0, nullBitSet.length); return true; } @Override public void close() { page.buffer().release(); page = SENTINEL; } /* TODO for variable-length types public VariableLengthDataReader getVariableLengthData(int columnIndex, int variableLengthDataOffset) { return new VariableLengthDataReader(variableLengthDataOffset); } public class VariableLengthDataReader { private int offsetFromPosition; VariableLengthDataReader(int offsetFromPosition) { this.offsetFromPosition = offsetFromPosition; } public byte readByte() { byte value = page.getByte(position + offsetFromPosition); offsetFromPosition += 1; return value; } public short readShort() { short value = page.getShort(position + offsetFromPosition); offsetFromPosition += 2; return value; } public int readInt() { int value = page.getInt(position + offsetFromPosition); offsetFromPosition += 4; return value; } public long readLong() { long value = page.getLong(position + offsetFromPosition); offsetFromPosition += 8; return value; } public float readFloat() { float value = page.getFloat(position + offsetFromPosition); offsetFromPosition += 4; return value; } public double readDouble() { double value = page.getDouble(position + offsetFromPosition); offsetFromPosition += 8; return value; } public void readBytes(byte[] data) { readBytes(data, 0, data.length); } public void readBytes(byte[] data, int off, int len) { page.getBytes(position + offsetFromPosition, data, off, len); offsetFromPosition += len; } } */ }