/*
* 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.hbase.cell;
import java.io.Serializable;
import java.util.Comparator;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hbase.Cell;
import com.google.common.primitives.Longs;
/**
* Compare two traditional HBase cells.
*
* Note: This comparator is not valid for -ROOT- and .META. tables.
*/
@InterfaceAudience.Private
@InterfaceStability.Evolving
public class CellComparator implements Comparator<Cell>, Serializable{
private static final long serialVersionUID = -8760041766259623329L;
@Override
public int compare(Cell a, Cell b) {
return compareStatic(a, b);
}
public static int compareStatic(Cell a, Cell b) {
//row
int c = Bytes.compareTo(
a.getRowArray(), a.getRowOffset(), a.getRowLength(),
b.getRowArray(), b.getRowOffset(), b.getRowLength());
if (c != 0) return c;
//family
c = Bytes.compareTo(
a.getFamilyArray(), a.getFamilyOffset(), a.getFamilyLength(),
b.getFamilyArray(), b.getFamilyOffset(), b.getFamilyLength());
if (c != 0) return c;
//qualifier
c = Bytes.compareTo(
a.getQualifierArray(), a.getQualifierOffset(), a.getQualifierLength(),
b.getQualifierArray(), b.getQualifierOffset(), b.getQualifierLength());
if (c != 0) return c;
//timestamp: later sorts first
c = -Longs.compare(a.getTimestamp(), b.getTimestamp());
if (c != 0) return c;
//type
c = (0xff & a.getTypeByte()) - (0xff & b.getTypeByte());
if (c != 0) return c;
//mvccVersion: later sorts first
return -Longs.compare(a.getMvccVersion(), b.getMvccVersion());
}
/**************** equals ****************************/
public static boolean equals(Cell a, Cell b){
if (!areKeyLengthsEqual(a, b)) {
return false;
}
//TODO compare byte[]'s in reverse since later bytes more likely to differ
return 0 == compareStatic(a, b);
}
public static boolean equalsRow(Cell a, Cell b){
if(!areRowLengthsEqual(a, b)){
return false;
}
return 0 == Bytes.compareTo(
a.getRowArray(), a.getRowOffset(), a.getRowLength(),
b.getRowArray(), b.getRowOffset(), b.getRowLength());
}
/********************* hashCode ************************/
/**
* Returns a hash code that is always the same for two Cells having a matching equals(..) result.
* Currently does not guard against nulls, but it could if necessary.
*/
public static int hashCode(Cell cell){
if (cell == null) {// return 0 for empty Cell
return 0;
}
//pre-calculate the 3 hashes made of byte ranges
int rowHash = Bytes.hashCode(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
int familyHash = Bytes.hashCode(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
int qualifierHash = Bytes.hashCode(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
//combine the 6 sub-hashes
int hash = 31 * rowHash + familyHash;
hash = 31 * hash + qualifierHash;
hash = 31 * hash + (int)cell.getTimestamp();
hash = 31 * hash + cell.getTypeByte();
hash = 31 * hash + (int)cell.getMvccVersion();
return hash;
}
/******************** lengths *************************/
public static boolean areKeyLengthsEqual(Cell a, Cell b) {
return a.getRowLength() == b.getRowLength()
&& a.getFamilyLength() == b.getFamilyLength()
&& a.getQualifierLength() == b.getQualifierLength();
}
public static boolean areRowLengthsEqual(Cell a, Cell b) {
return a.getRowLength() == b.getRowLength();
}
/***************** special cases ****************************/
/**
* special case for KeyValue.equals
*/
private static int compareStaticIgnoreMvccVersion(Cell a, Cell b) {
//row
int c = Bytes.compareTo(
a.getRowArray(), a.getRowOffset(), a.getRowLength(),
b.getRowArray(), b.getRowOffset(), b.getRowLength());
if (c != 0) return c;
//family
c = Bytes.compareTo(
a.getFamilyArray(), a.getFamilyOffset(), a.getFamilyLength(),
b.getFamilyArray(), b.getFamilyOffset(), b.getFamilyLength());
if (c != 0) return c;
//qualifier
c = Bytes.compareTo(
a.getQualifierArray(), a.getQualifierOffset(), a.getQualifierLength(),
b.getQualifierArray(), b.getQualifierOffset(), b.getQualifierLength());
if (c != 0) return c;
//timestamp: later sorts first
c = -Longs.compare(a.getTimestamp(), b.getTimestamp());
if (c != 0) return c;
//type
c = (0xff & a.getTypeByte()) - (0xff & b.getTypeByte());
return c;
}
/**
* special case for KeyValue.equals
*/
public static boolean equalsIgnoreMvccVersion(Cell a, Cell b){
return 0 == compareStaticIgnoreMvccVersion(a, b);
}
}