package com.facebook.infrastructure.db; import com.facebook.infrastructure.io.DataInputBuffer; import com.facebook.infrastructure.io.DataOutputBuffer; import com.facebook.infrastructure.io.IFileReader; import com.facebook.infrastructure.io.SSTable; import java.io.IOException; import java.util.Iterator; public class FileStruct implements Comparable<FileStruct>, Iterable<String> { private String key = null; private boolean exhausted = false; private IFileReader reader; private DataInputBuffer bufIn; private DataOutputBuffer bufOut; public FileStruct(IFileReader reader) { this.reader = reader; bufIn = new DataInputBuffer(); bufOut = new DataOutputBuffer(); } public String getFileName() { return reader.getFileName(); } public void close() throws IOException { reader.close(); } public boolean isExhausted() { return exhausted; } public DataInputBuffer getBufIn() { return bufIn; } public String getKey() { return key; } public int compareTo(FileStruct f) { return key.compareTo(f.key); } // we don't use SequenceReader.seekTo, since that (sometimes) throws an exception // if the key is not found. unsure if this behavior is desired. public void seekTo(String seekKey) { try { SSTable.Range range = SSTable.getRange(seekKey, reader); reader.seek(range.end); long position = reader.getPositionFromBlockIndex(seekKey); if (position == -1) { reader.seek(range.start); } else { reader.seek(position); } while (!exhausted) { getNextKey(); if (key.compareTo(seekKey) >= 0) { break; } } } catch (IOException e) { throw new RuntimeException("corrupt sstable", e); } } /* * Read the next key from the data file, skipping block indexes. * Caller must check isExhausted after each call to see if further * reads are valid. */ public void getNextKey() { if (exhausted) { throw new IndexOutOfBoundsException(); } try { bufOut.reset(); if (reader.isEOF()) { reader.close(); exhausted = true; return; } long bytesread = reader.next(bufOut); if (bytesread == -1) { reader.close(); exhausted = true; return; } bufIn.reset(bufOut.getData(), bufOut.getLength()); key = bufIn.readUTF(); /* If the key we read is the Block Index Key then omit and read the next key. */ if ( key.equals(SSTable.blockIndexKey_) ) { bufOut.reset(); bytesread = reader.next(bufOut); if (bytesread == -1) { reader.close(); exhausted = true; return; } bufIn.reset(bufOut.getData(), bufOut.getLength()); key = bufIn.readUTF(); } } catch (IOException e) { throw new RuntimeException(e); } } public Iterator<String> iterator() { return new FileStructIterator(); } private class FileStructIterator implements Iterator<String> { String saved; public FileStructIterator() { if (getKey() == null && !isExhausted()) { forward(); } } private void forward() { getNextKey(); saved = isExhausted() ? null : getKey(); } public boolean hasNext() { return saved != null; } public String next() { if (saved == null) { throw new IndexOutOfBoundsException(); } String key = saved; forward(); return key; } public void remove() { throw new UnsupportedOperationException(); } } }