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 java.nio.ByteBuffer;
021import org.apache.hadoop.hbase.ByteBufferExtendedCell;
022import org.apache.hadoop.hbase.Cell;
023import org.apache.hadoop.hbase.unsafe.HBasePlatformDependent;
024import org.apache.yetus.audience.InterfaceAudience;
025
026/**
027 * Utility methods for reading and writing little-endian integers from byte[] and ByteBuffer. Used
028 * by hashing components to perform fast, low-level LE conversions with optional Unsafe
029 * acceleration.
030 */
031@InterfaceAudience.Private
032public final class LittleEndianBytes {
033  final static boolean UNSAFE_UNALIGNED = HBasePlatformDependent.unaligned();
034
035  static abstract class Converter {
036    abstract int toInt(byte[] bytes, int offset);
037
038    abstract int toInt(ByteBuffer buffer, int offset);
039
040    abstract int putInt(byte[] bytes, int offset, int val);
041  }
042
043  static class ConverterHolder {
044    static final String UNSAFE_CONVERTER_NAME =
045      ConverterHolder.class.getName() + "$UnsafeConverter";
046    static final Converter BEST_CONVERTER = getBestConverter();
047
048    static Converter getBestConverter() {
049      try {
050        Class<? extends Converter> theClass =
051          Class.forName(UNSAFE_CONVERTER_NAME).asSubclass(Converter.class);
052        return theClass.getConstructor().newInstance();
053      } catch (Throwable t) {
054        return PureJavaConverter.INSTANCE;
055      }
056    }
057
058    static final class PureJavaConverter extends Converter {
059      static final PureJavaConverter INSTANCE = new PureJavaConverter();
060
061      private PureJavaConverter() {
062      }
063
064      @Override
065      int toInt(byte[] bytes, int offset) {
066        return (bytes[offset] & 0xFF) | ((bytes[offset + 1] & 0xFF) << 8)
067          | ((bytes[offset + 2] & 0xFF) << 16) | ((bytes[offset + 3] & 0xFF) << 24);
068      }
069
070      @Override
071      int toInt(ByteBuffer buffer, int offset) {
072        return (buffer.get(offset) & 0xFF) | ((buffer.get(offset + 1) & 0xFF) << 8)
073          | ((buffer.get(offset + 2) & 0xFF) << 16) | ((buffer.get(offset + 3) & 0xFF) << 24);
074      }
075
076      @Override
077      int putInt(byte[] bytes, int offset, int val) {
078        bytes[offset] = (byte) val;
079        bytes[offset + 1] = (byte) (val >>> 8);
080        bytes[offset + 2] = (byte) (val >>> 16);
081        bytes[offset + 3] = (byte) (val >>> 24);
082        return offset + Bytes.SIZEOF_INT;
083      }
084    }
085
086    static final class UnsafeConverter extends Converter {
087      static final UnsafeConverter INSTANCE = new UnsafeConverter();
088
089      public UnsafeConverter() {
090      }
091
092      static {
093        if (!UNSAFE_UNALIGNED) {
094          throw new Error();
095        }
096      }
097
098      @Override
099      int toInt(byte[] bytes, int offset) {
100        return UnsafeAccess.toIntLE(bytes, offset);
101      }
102
103      @Override
104      int toInt(ByteBuffer buffer, int offset) {
105        return UnsafeAccess.toIntLE(buffer, offset);
106      }
107
108      @Override
109      int putInt(byte[] bytes, int offset, int val) {
110        return UnsafeAccess.putIntLE(bytes, offset, val);
111      }
112    }
113  }
114
115  /*
116   * Writes an int in little-endian order. Caller must ensure bounds; no checks are performed.
117   */
118  public static void putInt(byte[] bytes, int offset, int val) {
119    assert offset >= 0 && bytes.length - offset >= Bytes.SIZEOF_INT;
120    ConverterHolder.BEST_CONVERTER.putInt(bytes, offset, val);
121  }
122
123  /*
124   * Reads an int in little-endian order. Caller must ensure bounds; no checks are performed.
125   */
126  public static int toInt(byte[] bytes, int offset) {
127    assert offset >= 0 && bytes.length - offset >= Bytes.SIZEOF_INT;
128    return ConverterHolder.BEST_CONVERTER.toInt(bytes, offset);
129  }
130
131  /*
132   * Reads an int in little-endian order from ByteBuffer. Caller must ensure bounds; no checks are
133   * performed.
134   */
135  public static int toInt(ByteBuffer buffer, int offset) {
136    assert offset >= 0 && buffer.capacity() - offset >= Bytes.SIZEOF_INT;
137    return ConverterHolder.BEST_CONVERTER.toInt(buffer, offset);
138  }
139
140  /*
141   * Reads an int in little-endian order from the row portion of the Cell, at the given offset.
142   */
143  public static int getRowAsInt(Cell cell, int offset) {
144    if (cell instanceof ByteBufferExtendedCell) {
145      ByteBufferExtendedCell bbCell = (ByteBufferExtendedCell) cell;
146      return toInt(bbCell.getRowByteBuffer(), bbCell.getRowPosition() + offset);
147    }
148    return toInt(cell.getRowArray(), cell.getRowOffset() + offset);
149  }
150
151  /*
152   * Reads an int in little-endian order from the qualifier portion of the Cell, at the given
153   * offset.
154   */
155  public static int getQualifierAsInt(Cell cell, int offset) {
156    if (cell instanceof ByteBufferExtendedCell) {
157      ByteBufferExtendedCell bbCell = (ByteBufferExtendedCell) cell;
158      return toInt(bbCell.getQualifierByteBuffer(), bbCell.getQualifierPosition() + offset);
159    }
160    return toInt(cell.getQualifierArray(), cell.getQualifierOffset() + offset);
161  }
162
163  private LittleEndianBytes() {
164  }
165}