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