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