blob: dea48fd7a094d29c3ecce6fbf54f85c26664dc90 [file] [log] [blame]
/*
* 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.accumulo.core.client.admin;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.Map.Entry;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.Scanner;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.PartialKey;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.iterators.SortedKeyIterator;
import org.apache.hadoop.io.Text;
public class FindMax {
private static void appendZeros(ByteArrayOutputStream baos, int num) {
for (int i = 0; i < num; i++)
baos.write(0);
}
private static Text findMidPoint(Text minBS, Text maxBS) {
ByteArrayOutputStream startOS = new ByteArrayOutputStream();
startOS.write(0); // add a leading zero so bigint does not think its negative
startOS.write(minBS.getBytes(), 0, minBS.getLength());
ByteArrayOutputStream endOS = new ByteArrayOutputStream();
endOS.write(0);// add a leading zero so bigint does not think its negative
endOS.write(maxBS.getBytes(), 0, maxBS.getLength());
// make the numbers of the same magnitude
if (startOS.size() < endOS.size())
appendZeros(startOS, endOS.size() - startOS.size());
else if (endOS.size() < startOS.size())
appendZeros(endOS, startOS.size() - endOS.size());
BigInteger min = new BigInteger(startOS.toByteArray());
BigInteger max = new BigInteger(endOS.toByteArray());
BigInteger mid = max.subtract(min).divide(BigInteger.valueOf(2)).add(min);
byte[] ba = mid.toByteArray();
Text ret = new Text();
if (ba.length == startOS.size()) {
if (ba[0] != 0)
throw new RuntimeException();
// big int added a zero so it would not be negative, drop it
ret.set(ba, 1, ba.length - 1);
} else {
int expLen = Math.max(minBS.getLength(), maxBS.getLength());
// big int will drop leading 0x0 bytes
for (int i = ba.length; i < expLen; i++) {
ret.append(new byte[] {0}, 0, 1);
}
ret.append(ba, 0, ba.length);
}
// remove trailing 0x0 bytes
while (ret.getLength() > 0 && ret.getBytes()[ret.getLength() - 1] == 0
&& ret.compareTo(minBS) > 0) {
Text t = new Text();
t.set(ret.getBytes(), 0, ret.getLength() - 1);
ret = t;
}
return ret;
}
private static Text _findMax(Scanner scanner, Text start, boolean inclStart, Text end,
boolean inclEnd) {
// System.out.printf("findMax(%s, %s, %s, %s)%n", Key.toPrintableString(start.getBytes(), 0,
// start.getLength(), 1000), inclStart,
// Key.toPrintableString(end.getBytes(), 0, end.getLength(), 1000), inclEnd);
int cmp = start.compareTo(end);
if (cmp >= 0) {
if (inclStart && inclEnd && cmp == 0) {
scanner.setRange(new Range(start, true, end, true));
Iterator<Entry<Key,Value>> iter = scanner.iterator();
if (iter.hasNext())
return iter.next().getKey().getRow();
}
return null;
}
Text mid = findMidPoint(start, end);
// System.out.println("mid = :"+Key.toPrintableString(mid.getBytes(), 0, mid.getLength(),
// 1000)+":");
scanner.setRange(new Range(mid, mid.equals(start) ? inclStart : true, end, inclEnd));
Iterator<Entry<Key,Value>> iter = scanner.iterator();
if (iter.hasNext()) {
Key next = iter.next().getKey();
int count = 0;
while (count < 10 && iter.hasNext()) {
next = iter.next().getKey();
count++;
}
if (!iter.hasNext())
return next.getRow();
Text ret = _findMax(scanner, next.followingKey(PartialKey.ROW).getRow(), true, end, inclEnd);
if (ret == null)
return next.getRow();
else
return ret;
} else {
return _findMax(scanner, start, inclStart, mid, mid.equals(start) ? inclStart : false);
}
}
private static Text findInitialEnd(Scanner scanner) {
Text end = new Text(new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff});
scanner.setRange(new Range(end, null));
while (scanner.iterator().hasNext()) {
Text t = new Text();
t.append(end.getBytes(), 0, end.getLength());
t.append(end.getBytes(), 0, end.getLength());
end = t;
scanner.setRange(new Range(end, null));
}
return end;
}
public static Text findMax(Scanner scanner, Text start, boolean is, Text end, boolean ie)
throws TableNotFoundException {
scanner.setBatchSize(12);
IteratorSetting cfg = new IteratorSetting(Integer.MAX_VALUE, SortedKeyIterator.class);
scanner.addScanIterator(cfg);
if (start == null) {
start = new Text();
is = true;
}
if (end == null)
end = findInitialEnd(scanner);
return _findMax(scanner, start, is, end, ie);
}
}