|
@@ -669,10 +669,10 @@ public class TFile {
|
|
* TFile Reader. Users may only read TFiles by creating TFile.Reader.Scanner.
|
|
* TFile Reader. Users may only read TFiles by creating TFile.Reader.Scanner.
|
|
* objects. A scanner may scan the whole TFile ({@link Reader#createScanner()}
|
|
* objects. A scanner may scan the whole TFile ({@link Reader#createScanner()}
|
|
* ) , a portion of TFile based on byte offsets (
|
|
* ) , a portion of TFile based on byte offsets (
|
|
- * {@link Reader#createScanner(long, long)}), or a portion of TFile with keys
|
|
|
|
|
|
+ * {@link Reader#createScannerByByteRange(long, long)}), or a portion of TFile with keys
|
|
* fall in a certain key range (for sorted TFile only,
|
|
* fall in a certain key range (for sorted TFile only,
|
|
- * {@link Reader#createScanner(byte[], byte[])} or
|
|
|
|
- * {@link Reader#createScanner(RawComparable, RawComparable)}).
|
|
|
|
|
|
+ * {@link Reader#createScannerByKey(byte[], byte[])} or
|
|
|
|
+ * {@link Reader#createScannerByKey(RawComparable, RawComparable)}).
|
|
*/
|
|
*/
|
|
public static class Reader implements Closeable {
|
|
public static class Reader implements Closeable {
|
|
// The underlying BCFile reader.
|
|
// The underlying BCFile reader.
|
|
@@ -986,6 +986,16 @@ public class TFile {
|
|
return new Location(blkIndex, 0);
|
|
return new Location(blkIndex, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ Location getLocationByRecordNum(long recNum) throws IOException {
|
|
|
|
+ checkTFileDataIndex();
|
|
|
|
+ return tfileIndex.getLocationByRecordNum(recNum);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ long getRecordNumByLocation(Location location) throws IOException {
|
|
|
|
+ checkTFileDataIndex();
|
|
|
|
+ return tfileIndex.getRecordNumByLocation(location);
|
|
|
|
+ }
|
|
|
|
+
|
|
int compareKeys(byte[] a, int o1, int l1, byte[] b, int o2, int l2) {
|
|
int compareKeys(byte[] a, int o1, int l1, byte[] b, int o2, int l2) {
|
|
if (!isSorted()) {
|
|
if (!isSorted()) {
|
|
throw new RuntimeException("Cannot compare keys for unsorted TFiles.");
|
|
throw new RuntimeException("Cannot compare keys for unsorted TFiles.");
|
|
@@ -1016,6 +1026,21 @@ public class TFile {
|
|
return new Location(blockIndex, 0);
|
|
return new Location(blockIndex, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Get the RecordNum for the first key-value pair in a compressed block
|
|
|
|
+ * whose byte offset in the TFile is greater than or equal to the specified
|
|
|
|
+ * offset.
|
|
|
|
+ *
|
|
|
|
+ * @param offset
|
|
|
|
+ * the user supplied offset.
|
|
|
|
+ * @return the RecordNum to the corresponding entry. If no such entry
|
|
|
|
+ * exists, it returns the total entry count.
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ public long getRecordNumNear(long offset) throws IOException {
|
|
|
|
+ return getRecordNumByLocation(getLocationNear(offset));
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Get a sample key that is within a block whose starting offset is greater
|
|
* Get a sample key that is within a block whose starting offset is greater
|
|
* than or equal to the specified offset.
|
|
* than or equal to the specified offset.
|
|
@@ -1058,7 +1083,7 @@ public class TFile {
|
|
* contains zero key-value pairs even if length is positive.
|
|
* contains zero key-value pairs even if length is positive.
|
|
* @throws IOException
|
|
* @throws IOException
|
|
*/
|
|
*/
|
|
- public Scanner createScanner(long offset, long length) throws IOException {
|
|
|
|
|
|
+ public Scanner createScannerByByteRange(long offset, long length) throws IOException {
|
|
return new Scanner(this, offset, offset + length);
|
|
return new Scanner(this, offset, offset + length);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1074,10 +1099,31 @@ public class TFile {
|
|
* @return The actual coverage of the returned scanner will cover all keys
|
|
* @return The actual coverage of the returned scanner will cover all keys
|
|
* greater than or equal to the beginKey and less than the endKey.
|
|
* greater than or equal to the beginKey and less than the endKey.
|
|
* @throws IOException
|
|
* @throws IOException
|
|
|
|
+ *
|
|
|
|
+ * @deprecated Use {@link #createScannerByKey(byte[], byte[])} instead.
|
|
*/
|
|
*/
|
|
|
|
+ @Deprecated
|
|
public Scanner createScanner(byte[] beginKey, byte[] endKey)
|
|
public Scanner createScanner(byte[] beginKey, byte[] endKey)
|
|
|
|
+ throws IOException {
|
|
|
|
+ return createScannerByKey(beginKey, endKey);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get a scanner that covers a portion of TFile based on keys.
|
|
|
|
+ *
|
|
|
|
+ * @param beginKey
|
|
|
|
+ * Begin key of the scan (inclusive). If null, scan from the first
|
|
|
|
+ * key-value entry of the TFile.
|
|
|
|
+ * @param endKey
|
|
|
|
+ * End key of the scan (exclusive). If null, scan up to the last
|
|
|
|
+ * key-value entry of the TFile.
|
|
|
|
+ * @return The actual coverage of the returned scanner will cover all keys
|
|
|
|
+ * greater than or equal to the beginKey and less than the endKey.
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ public Scanner createScannerByKey(byte[] beginKey, byte[] endKey)
|
|
throws IOException {
|
|
throws IOException {
|
|
- return createScanner((beginKey == null) ? null : new ByteArray(beginKey,
|
|
|
|
|
|
+ return createScannerByKey((beginKey == null) ? null : new ByteArray(beginKey,
|
|
0, beginKey.length), (endKey == null) ? null : new ByteArray(endKey,
|
|
0, beginKey.length), (endKey == null) ? null : new ByteArray(endKey,
|
|
0, endKey.length));
|
|
0, endKey.length));
|
|
}
|
|
}
|
|
@@ -1094,9 +1140,31 @@ public class TFile {
|
|
* @return The actual coverage of the returned scanner will cover all keys
|
|
* @return The actual coverage of the returned scanner will cover all keys
|
|
* greater than or equal to the beginKey and less than the endKey.
|
|
* greater than or equal to the beginKey and less than the endKey.
|
|
* @throws IOException
|
|
* @throws IOException
|
|
|
|
+ *
|
|
|
|
+ * @deprecated Use {@link #createScannerByKey(RawComparable, RawComparable)}
|
|
|
|
+ * instead.
|
|
*/
|
|
*/
|
|
|
|
+ @Deprecated
|
|
public Scanner createScanner(RawComparable beginKey, RawComparable endKey)
|
|
public Scanner createScanner(RawComparable beginKey, RawComparable endKey)
|
|
throws IOException {
|
|
throws IOException {
|
|
|
|
+ return createScannerByKey(beginKey, endKey);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get a scanner that covers a specific key range.
|
|
|
|
+ *
|
|
|
|
+ * @param beginKey
|
|
|
|
+ * Begin key of the scan (inclusive). If null, scan from the first
|
|
|
|
+ * key-value entry of the TFile.
|
|
|
|
+ * @param endKey
|
|
|
|
+ * End key of the scan (exclusive). If null, scan up to the last
|
|
|
|
+ * key-value entry of the TFile.
|
|
|
|
+ * @return The actual coverage of the returned scanner will cover all keys
|
|
|
|
+ * greater than or equal to the beginKey and less than the endKey.
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ public Scanner createScannerByKey(RawComparable beginKey, RawComparable endKey)
|
|
|
|
+ throws IOException {
|
|
if ((beginKey != null) && (endKey != null)
|
|
if ((beginKey != null) && (endKey != null)
|
|
&& (compareKeys(beginKey, endKey) >= 0)) {
|
|
&& (compareKeys(beginKey, endKey) >= 0)) {
|
|
return new Scanner(this, beginKey, beginKey);
|
|
return new Scanner(this, beginKey, beginKey);
|
|
@@ -1104,6 +1172,27 @@ public class TFile {
|
|
return new Scanner(this, beginKey, endKey);
|
|
return new Scanner(this, beginKey, endKey);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Create a scanner that covers a range of records.
|
|
|
|
+ *
|
|
|
|
+ * @param beginRecNum
|
|
|
|
+ * The RecordNum for the first record (inclusive).
|
|
|
|
+ * @param endRecNum
|
|
|
|
+ * The RecordNum for the last record (exclusive). To scan the whole
|
|
|
|
+ * file, either specify endRecNum==-1 or endRecNum==getEntryCount().
|
|
|
|
+ * @return The TFile scanner that covers the specified range of records.
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ public Scanner createScannerByRecordNum(long beginRecNum, long endRecNum)
|
|
|
|
+ throws IOException {
|
|
|
|
+ if (beginRecNum < 0) beginRecNum = 0;
|
|
|
|
+ if (endRecNum < 0 || endRecNum > getEntryCount()) {
|
|
|
|
+ endRecNum = getEntryCount();
|
|
|
|
+ }
|
|
|
|
+ return new Scanner(this, getLocationByRecordNum(beginRecNum),
|
|
|
|
+ getLocationByRecordNum(endRecNum));
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* The TFile Scanner. The Scanner has an implicit cursor, which, upon
|
|
* The TFile Scanner. The Scanner has an implicit cursor, which, upon
|
|
* creation, points to the first key-value pair in the scan range. If the
|
|
* creation, points to the first key-value pair in the scan range. If the
|
|
@@ -1523,6 +1612,15 @@ public class TFile {
|
|
return new Entry();
|
|
return new Entry();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Get the RecordNum corresponding to the entry pointed by the cursor.
|
|
|
|
+ * @return The RecordNum corresponding to the entry pointed by the cursor.
|
|
|
|
+ * @throws IOException
|
|
|
|
+ */
|
|
|
|
+ public long getRecordNum() throws IOException {
|
|
|
|
+ return reader.getRecordNumByLocation(currentLocation);
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Internal API. Comparing the key at cursor to user-specified key.
|
|
* Internal API. Comparing the key at cursor to user-specified key.
|
|
*
|
|
*
|
|
@@ -2021,8 +2119,10 @@ public class TFile {
|
|
final static String BLOCK_NAME = "TFile.index";
|
|
final static String BLOCK_NAME = "TFile.index";
|
|
private ByteArray firstKey;
|
|
private ByteArray firstKey;
|
|
private final ArrayList<TFileIndexEntry> index;
|
|
private final ArrayList<TFileIndexEntry> index;
|
|
|
|
+ private final ArrayList<Long> recordNumIndex;
|
|
private final BytesComparator comparator;
|
|
private final BytesComparator comparator;
|
|
-
|
|
|
|
|
|
+ private long sum = 0;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* For reading from file.
|
|
* For reading from file.
|
|
*
|
|
*
|
|
@@ -2031,6 +2131,7 @@ public class TFile {
|
|
public TFileIndex(int entryCount, DataInput in, BytesComparator comparator)
|
|
public TFileIndex(int entryCount, DataInput in, BytesComparator comparator)
|
|
throws IOException {
|
|
throws IOException {
|
|
index = new ArrayList<TFileIndexEntry>(entryCount);
|
|
index = new ArrayList<TFileIndexEntry>(entryCount);
|
|
|
|
+ recordNumIndex = new ArrayList<Long>(entryCount);
|
|
int size = Utils.readVInt(in); // size for the first key entry.
|
|
int size = Utils.readVInt(in); // size for the first key entry.
|
|
if (size > 0) {
|
|
if (size > 0) {
|
|
byte[] buffer = new byte[size];
|
|
byte[] buffer = new byte[size];
|
|
@@ -2052,6 +2153,8 @@ public class TFile {
|
|
new TFileIndexEntry(new DataInputStream(new ByteArrayInputStream(
|
|
new TFileIndexEntry(new DataInputStream(new ByteArrayInputStream(
|
|
buffer, 0, size)));
|
|
buffer, 0, size)));
|
|
index.add(idx);
|
|
index.add(idx);
|
|
|
|
+ sum += idx.entries();
|
|
|
|
+ recordNumIndex.add(sum);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
if (entryCount != 0) {
|
|
if (entryCount != 0) {
|
|
@@ -2083,6 +2186,12 @@ public class TFile {
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * @param key
|
|
|
|
+ * input key.
|
|
|
|
+ * @return the ID of the first block that contains key > input key. Or -1
|
|
|
|
+ * if no such block exists.
|
|
|
|
+ */
|
|
public int upperBound(RawComparable key) {
|
|
public int upperBound(RawComparable key) {
|
|
if (comparator == null) {
|
|
if (comparator == null) {
|
|
throw new RuntimeException("Cannot search in unsorted TFile");
|
|
throw new RuntimeException("Cannot search in unsorted TFile");
|
|
@@ -2104,13 +2213,26 @@ public class TFile {
|
|
*/
|
|
*/
|
|
public TFileIndex(BytesComparator comparator) {
|
|
public TFileIndex(BytesComparator comparator) {
|
|
index = new ArrayList<TFileIndexEntry>();
|
|
index = new ArrayList<TFileIndexEntry>();
|
|
|
|
+ recordNumIndex = new ArrayList<Long>();
|
|
this.comparator = comparator;
|
|
this.comparator = comparator;
|
|
}
|
|
}
|
|
|
|
|
|
public RawComparable getFirstKey() {
|
|
public RawComparable getFirstKey() {
|
|
return firstKey;
|
|
return firstKey;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ public Reader.Location getLocationByRecordNum(long recNum) {
|
|
|
|
+ int idx = Utils.upperBound(recordNumIndex, recNum);
|
|
|
|
+ long lastRecNum = (idx == 0)? 0: recordNumIndex.get(idx-1);
|
|
|
|
+ return new Reader.Location(idx, recNum-lastRecNum);
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ public long getRecordNumByLocation(Reader.Location location) {
|
|
|
|
+ int blkIndex = location.getBlockIndex();
|
|
|
|
+ long lastRecNum = (blkIndex == 0) ? 0: recordNumIndex.get(blkIndex-1);
|
|
|
|
+ return lastRecNum + location.getRecordIndex();
|
|
|
|
+ }
|
|
|
|
+
|
|
public void setFirstKey(byte[] key, int offset, int length) {
|
|
public void setFirstKey(byte[] key, int offset, int length) {
|
|
firstKey = new ByteArray(new byte[length]);
|
|
firstKey = new ByteArray(new byte[length]);
|
|
System.arraycopy(key, offset, firstKey.buffer(), 0, length);
|
|
System.arraycopy(key, offset, firstKey.buffer(), 0, length);
|
|
@@ -2125,6 +2247,8 @@ public class TFile {
|
|
|
|
|
|
public void addEntry(TFileIndexEntry keyEntry) {
|
|
public void addEntry(TFileIndexEntry keyEntry) {
|
|
index.add(keyEntry);
|
|
index.add(keyEntry);
|
|
|
|
+ sum += keyEntry.entries();
|
|
|
|
+ recordNumIndex.add(sum);
|
|
}
|
|
}
|
|
|
|
|
|
public TFileIndexEntry getEntry(int bid) {
|
|
public TFileIndexEntry getEntry(int bid) {
|