001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.filter;
019
020import java.nio.ByteBuffer;
021import org.apache.hadoop.hbase.exceptions.DeserializationException;
022import org.apache.yetus.audience.InterfaceAudience;
023
024import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
025
026import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
027import org.apache.hadoop.hbase.shaded.protobuf.generated.ComparatorProtos;
028
029/**
030 * A bit comparator which performs the specified bitwise operation on each of the bytes with the
031 * specified byte array. Then returns whether the result is non-zero.
032 */
033@InterfaceAudience.Public
034@SuppressWarnings("ComparableType") // Should this move to Comparator usage?
035public class BitComparator extends ByteArrayComparable {
036
037  /** Bit operators. */
038  @InterfaceAudience.Public
039  public enum BitwiseOp {
040    /** and */
041    AND,
042    /** or */
043    OR,
044    /** xor */
045    XOR
046  }
047
048  protected BitwiseOp bitOperator;
049
050  /**
051   * Constructor
052   * @param value       value
053   * @param bitOperator operator to use on the bit comparison
054   */
055  public BitComparator(byte[] value, BitwiseOp bitOperator) {
056    super(value);
057    this.bitOperator = bitOperator;
058  }
059
060  /**
061   * @return the bitwise operator
062   */
063  public BitwiseOp getOperator() {
064    return bitOperator;
065  }
066
067  /**
068   * @return The comparator serialized using pb
069   */
070  @Override
071  public byte[] toByteArray() {
072    ComparatorProtos.BitComparator.Builder builder = ComparatorProtos.BitComparator.newBuilder();
073    builder.setComparable(ProtobufUtil.toByteArrayComparable(this.value));
074    ComparatorProtos.BitComparator.BitwiseOp bitwiseOpPb =
075      ComparatorProtos.BitComparator.BitwiseOp.valueOf(bitOperator.name());
076    builder.setBitwiseOp(bitwiseOpPb);
077    return builder.build().toByteArray();
078  }
079
080  /**
081   * @param pbBytes A pb serialized {@link BitComparator} instance
082   * @return An instance of {@link BitComparator} made from <code>bytes</code> n * @see #toByteArray
083   */
084  public static BitComparator parseFrom(final byte[] pbBytes) throws DeserializationException {
085    ComparatorProtos.BitComparator proto;
086    try {
087      proto = ComparatorProtos.BitComparator.parseFrom(pbBytes);
088    } catch (InvalidProtocolBufferException e) {
089      throw new DeserializationException(e);
090    }
091    BitwiseOp bitwiseOp = BitwiseOp.valueOf(proto.getBitwiseOp().name());
092    return new BitComparator(proto.getComparable().getValue().toByteArray(), bitwiseOp);
093  }
094
095  /**
096   * n * @return true if and only if the fields of the comparator that are serialized are equal to
097   * the corresponding fields in other. Used for testing.
098   */
099  @Override
100  boolean areSerializedFieldsEqual(ByteArrayComparable other) {
101    if (other == this) return true;
102    if (!(other instanceof BitComparator)) return false;
103
104    BitComparator comparator = (BitComparator) other;
105    return super.areSerializedFieldsEqual(other)
106      && this.getOperator().equals(comparator.getOperator());
107  }
108
109  @Override
110  public int compareTo(byte[] value, int offset, int length) {
111    if (length != this.value.length) {
112      return 1;
113    }
114    int b = 0;
115    // Iterating backwards is faster because we can quit after one non-zero byte.
116    for (int i = length - 1; i >= 0 && b == 0; i--) {
117      switch (bitOperator) {
118        case AND:
119          b = (this.value[i] & value[i + offset]) & 0xff;
120          break;
121        case OR:
122          b = (this.value[i] | value[i + offset]) & 0xff;
123          break;
124        case XOR:
125          b = (this.value[i] ^ value[i + offset]) & 0xff;
126          break;
127      }
128    }
129    return b == 0 ? 1 : 0;
130  }
131
132  @Override
133  public int compareTo(ByteBuffer value, int offset, int length) {
134    if (length != this.value.length) {
135      return 1;
136    }
137    int b = 0;
138    // Iterating backwards is faster because we can quit after one non-zero byte.
139    for (int i = length - 1; i >= 0 && b == 0; i--) {
140      switch (bitOperator) {
141        case AND:
142          b = (this.value[i] & value.get(i + offset)) & 0xff;
143          break;
144        case OR:
145          b = (this.value[i] | value.get(i + offset)) & 0xff;
146          break;
147        case XOR:
148          b = (this.value[i] ^ value.get(i + offset)) & 0xff;
149          break;
150      }
151    }
152    return b == 0 ? 1 : 0;
153  }
154}