| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.trevni; |
| |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.util.Iterator; |
| |
| /** An iterator over column values. */ |
| public class ColumnValues<T extends Comparable> |
| implements Iterator<T>, Iterable<T> { |
| |
| private final ColumnDescriptor column; |
| private final ValueType type; |
| private final Codec codec; |
| private final Checksum checksum; |
| private final InputBuffer in; |
| |
| private InputBuffer values; |
| private int block = -1; |
| private long row = 0; |
| private T previous; |
| |
| private int arrayLength; |
| |
| ColumnValues(ColumnDescriptor column) throws IOException { |
| this.column = column; |
| this.type = column.metaData.getType(); |
| this.codec = Codec.get(column.metaData); |
| this.checksum = Checksum.get(column.metaData); |
| this.in = new InputBuffer(column.file); |
| |
| column.ensureBlocksRead(); |
| } |
| |
| /** Return the current row number within this file. */ |
| public long getRow() { return row; } |
| |
| /** Seek to the named row. */ |
| public void seek(long r) throws IOException { |
| if (r < row || r >= column.lastRow(block)) // not in current block |
| startBlock(column.findBlock(r)); // seek to block start |
| while (r > row && hasNext()) { // skip within block |
| values.skipValue(type); |
| row++; |
| } |
| previous = null; |
| } |
| |
| /** Seek to the named value. */ |
| public void seek(T v) throws IOException { |
| if (!column.metaData.hasIndexValues()) |
| throw new TrevniRuntimeException |
| ("Column does not have value index: " +column.metaData.getName()); |
| |
| if (previous == null // not in current block? |
| || previous.compareTo(v) > 0 |
| || (block != column.blockCount()-1 |
| && column.firstValues[block+1].compareTo(v) <= 0)) |
| startBlock(column.findBlock(v)); // seek to block start |
| |
| while (hasNext()) { // scan block |
| long savedPosition = values.tell(); |
| T savedPrevious = previous; |
| if (next().compareTo(v) >= 0) { |
| values.seek(savedPosition); |
| previous = savedPrevious; |
| row--; |
| return; |
| } |
| } |
| } |
| |
| private void startBlock(int block) throws IOException { |
| this.block = block; |
| this.row = column.firstRows[block]; |
| |
| in.seek(column.blockStarts[block]); |
| int end = column.blocks[block].compressedSize; |
| byte[] raw = new byte[end+checksum.size()]; |
| in.readFully(raw); |
| ByteBuffer data = codec.decompress(ByteBuffer.wrap(raw, 0, end)); |
| if (!checksum.compute(data).equals |
| (ByteBuffer.wrap(raw, end, checksum.size()))) |
| throw new IOException("Checksums mismatch."); |
| values = new InputBuffer(new InputBytes(data)); |
| } |
| |
| @Override public Iterator iterator() { return this; } |
| |
| @Override public boolean hasNext() { |
| return block < column.blockCount()-1 || row < column.lastRow(block); |
| } |
| |
| @Override public T next() { |
| if (column.metaData.isArray() || column.metaData.getParent() != null) |
| throw new TrevniRuntimeException |
| ("Column is array: " +column.metaData.getName()); |
| try { |
| startRow(); |
| return nextValue(); |
| } catch (IOException e) { |
| throw new TrevniRuntimeException(e); |
| } |
| } |
| |
| /** Expert: Must be called before any calls to {@link #nextLength()} or |
| * {@link #nextValue()}. */ |
| public void startRow() throws IOException { |
| if (row >= column.lastRow(block)) { |
| if (block >= column.blockCount()) |
| throw new TrevniRuntimeException("Read past end of column."); |
| startBlock(block+1); |
| } |
| row++; |
| } |
| |
| /** Expert: Returns the next length in an array column. */ |
| public int nextLength() throws IOException { |
| if (!column.metaData.isArray()) |
| throw new TrevniRuntimeException |
| ("Column is not array: " +column.metaData.getName()); |
| assert arrayLength == 0; |
| return arrayLength = values.readLength(); |
| } |
| |
| /** Expert: Returns the next value in a column. */ |
| public T nextValue() throws IOException { |
| arrayLength--; |
| return previous = values.<T>readValue(type); |
| } |
| |
| @Override public void remove() { throw new UnsupportedOperationException(); } |
| |
| } |