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.util;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021
022import org.apache.hadoop.hbase.HConstants;
023import org.apache.hadoop.hbase.KeyValue;
024import org.apache.hadoop.hbase.PrivateCellUtil;
025import org.apache.hadoop.hbase.testclassification.MiscTests;
026import org.apache.hadoop.hbase.testclassification.SmallTests;
027import org.junit.jupiter.api.BeforeEach;
028import org.junit.jupiter.api.Tag;
029import org.junit.jupiter.api.Test;
030
031@Tag(MiscTests.TAG)
032@Tag(SmallTests.TAG)
033public class TestRowColBloomHashKey {
034
035  private KeyValue kv;
036  private RowColBloomHashKey hashKey;
037
038  @BeforeEach
039  public void setup() {
040    byte[] row = Bytes.toBytes("row_key_test");
041    byte[] family = Bytes.toBytes("family");
042    byte[] qualifier = Bytes.toBytes("qualifier");
043    byte[] value = Bytes.toBytes("1234567890");
044    kv = new KeyValue(row, family, qualifier, value);
045    hashKey = new RowColBloomHashKey(kv);
046  }
047
048  @Test
049  public void testGet() {
050    final int rowLen = kv.getRowLength();
051    final int qualLen = kv.getQualifierLength();
052
053    // Expected virtual layout:
054    // [rowLen(2 bytes)][row bytes][famLen(1 byte, always 0)][qualifier bytes]
055    // [timestamp(8 bytes, HConstants.LATEST_TIMESTAMP)][type(1 byte, KeyValue.Type.Maximum)]
056    final int expectedLength = KeyValue.ROW_LENGTH_SIZE + rowLen + KeyValue.FAMILY_LENGTH_SIZE
057      + qualLen + KeyValue.TIMESTAMP_TYPE_SIZE;
058    assertEquals(expectedLength, hashKey.length());
059
060    int offset = 0;
061
062    // 1) Row length field: MSB then LSB
063    int msb = hashKey.get(offset++) & 0xFF;
064    int lsb = hashKey.get(offset++) & 0xFF;
065    int decodedRowLen = (msb << 8) | lsb;
066    assertEquals(rowLen, decodedRowLen);
067
068    // 2) Row bytes
069    for (int i = 0; i < rowLen; i++) {
070      int expected = PrivateCellUtil.getRowByte(kv, i) & 0xFF;
071      int actual = hashKey.get(offset++) & 0xFF;
072      assertEquals(expected, actual, "row byte mismatch at i=" + i);
073    }
074
075    // 3) Family length byte
076    assertEquals(0, hashKey.get(offset++) & 0xFF);
077
078    // 4) Qualifier bytes
079    for (int i = 0; i < qualLen; i++) {
080      int expected = PrivateCellUtil.getQualifierByte(kv, i) & 0xFF;
081      int actual = hashKey.get(offset++) & 0xFF;
082      assertEquals(expected, actual, "qualifier byte mismatch at i=" + i);
083    }
084
085    // 5) Timestamp bytes: should match HConstants.LATEST_TIMESTAMP in big-endian
086    // RowColBloomHashKey uses LATEST_TS byte[] from CellHashKey which corresponds to latest
087    // timestamp.
088    long ts = HConstants.LATEST_TIMESTAMP;
089    for (int i = 0; i < KeyValue.TIMESTAMP_SIZE; i++) {
090      // KeyValue timestamp serialization is big-endian
091      int expected = (int) ((ts >>> (8 * (KeyValue.TIMESTAMP_SIZE - 1 - i))) & 0xFF);
092      int actual = hashKey.get(offset++) & 0xFF;
093      assertEquals(expected, actual, "timestamp byte mismatch at i=" + i);
094    }
095
096    // 6) Type byte: should be Maximum
097    assertEquals(KeyValue.Type.Maximum.getCode(), hashKey.get(offset++));
098
099    // consumed exactly all bytes
100    assertEquals(hashKey.length(), offset);
101  }
102
103  @Test
104  public void testGetIntLE() {
105    for (int i = 0; i <= hashKey.length() - Bytes.SIZEOF_INT; i++) {
106      int expected = expectedIntLEFromGet(i);
107      int actual = hashKey.getIntLE(i);
108      assertEquals(expected, actual, "sequential mismatch at offset=" + i);
109    }
110  }
111
112  private int expectedIntLEFromGet(int offset) {
113    int b0 = hashKey.get(offset) & 0xFF;
114    int b1 = hashKey.get(offset + 1) & 0xFF;
115    int b2 = hashKey.get(offset + 2) & 0xFF;
116    int b3 = hashKey.get(offset + 3) & 0xFF;
117    return (b0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
118  }
119}