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  /** Returns the bitwise operator */
061  public BitwiseOp getOperator() {
062    return bitOperator;
063  }
064
065  /** Returns The comparator serialized using pb */
066  @Override
067  public byte[] toByteArray() {
068    ComparatorProtos.BitComparator.Builder builder = ComparatorProtos.BitComparator.newBuilder();
069    builder.setComparable(ProtobufUtil.toByteArrayComparable(this.value));
070    ComparatorProtos.BitComparator.BitwiseOp bitwiseOpPb =
071      ComparatorProtos.BitComparator.BitwiseOp.valueOf(bitOperator.name());
072    builder.setBitwiseOp(bitwiseOpPb);
073    return builder.build().toByteArray();
074  }
075
076  /**
077   * @param pbBytes A pb serialized {@link BitComparator} instance
078   * @return An instance of {@link BitComparator} made from <code>bytes</code>
079   * @see #toByteArray
080   */
081  public static BitComparator parseFrom(final byte[] pbBytes) throws DeserializationException {
082    ComparatorProtos.BitComparator proto;
083    try {
084      proto = ComparatorProtos.BitComparator.parseFrom(pbBytes);
085    } catch (InvalidProtocolBufferException e) {
086      throw new DeserializationException(e);
087    }
088    BitwiseOp bitwiseOp = BitwiseOp.valueOf(proto.getBitwiseOp().name());
089    return new BitComparator(proto.getComparable().getValue().toByteArray(), bitwiseOp);
090  }
091
092  /**
093   * @return true if and only if the fields of the comparator that are serialized are equal to the
094   *         corresponding fields in other. Used for testing.
095   */
096  @Override
097  boolean areSerializedFieldsEqual(ByteArrayComparable other) {
098    if (other == this) return true;
099    if (!(other instanceof BitComparator)) return false;
100
101    BitComparator comparator = (BitComparator) other;
102    return super.areSerializedFieldsEqual(other)
103      && this.getOperator().equals(comparator.getOperator());
104  }
105
106  @Override
107  public int compareTo(byte[] value, int offset, int length) {
108    if (length != this.value.length) {
109      return 1;
110    }
111    int b = 0;
112    // Iterating backwards is faster because we can quit after one non-zero byte.
113    for (int i = length - 1; i >= 0 && b == 0; i--) {
114      switch (bitOperator) {
115        case AND:
116          b = (this.value[i] & value[i + offset]) & 0xff;
117          break;
118        case OR:
119          b = (this.value[i] | value[i + offset]) & 0xff;
120          break;
121        case XOR:
122          b = (this.value[i] ^ value[i + offset]) & 0xff;
123          break;
124      }
125    }
126    return b == 0 ? 1 : 0;
127  }
128
129  @Override
130  public int compareTo(ByteBuffer value, int offset, int length) {
131    if (length != this.value.length) {
132      return 1;
133    }
134    int b = 0;
135    // Iterating backwards is faster because we can quit after one non-zero byte.
136    for (int i = length - 1; i >= 0 && b == 0; i--) {
137      switch (bitOperator) {
138        case AND:
139          b = (this.value[i] & value.get(i + offset)) & 0xff;
140          break;
141        case OR:
142          b = (this.value[i] | value.get(i + offset)) & 0xff;
143          break;
144        case XOR:
145          b = (this.value[i] ^ value.get(i + offset)) & 0xff;
146          break;
147      }
148    }
149    return b == 0 ? 1 : 0;
150  }
151}