| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.iotdb.tsfile.encoding.bitpacking; |
| |
| /** |
| * This class is used to encode(decode) Integer in Java with specified bit-width. User need to |
| * guarantee that the length of every given Integer in binary mode is less than or equal to the |
| * bit-width. |
| * |
| * <p>e.g., if bit-width is 4, then Integer '16'(10000)b is not allowed but '15'(1111)b is allowed. |
| */ |
| public class IntPacker { |
| /* |
| * For a full example, Width: 3 Input: 5 4 7 3 0 1 3 2 |
| * Output: |
| * +-----------------------+ +-----------------------+ +-----------------------+ |
| * |1 |0 |1 |1 |0 |0 |1 |1 | |1 |0 |1 |1 |0 |0 |0 |0 | |0 |1 |0 |1 |1 |0 |1 |0 | |
| * +-----------------------+ +-----------------------+ +-----------------------+ |
| * +-----+ +-----+ +---------+ +-----+ +-----+ +---------+ +-----+ +-----+ 5 4 7 |
| * 3 0 1 3 2 |
| */ |
| /** Number of Integers for each pack operation. */ |
| private static final int NUM_OF_INTS = 8; |
| /** bit-width. */ |
| private int width; |
| |
| public IntPacker(int width) { |
| this.width = width; |
| } |
| |
| /** |
| * Encode 8 ({@link IntPacker#NUM_OF_INTS}) Integers from the array 'values' with specified |
| * bit-width to bytes. |
| * |
| * @param values - array where '8 Integers' are in |
| * @param offset - the offset of first Integer to be encoded |
| * @param buf - encoded bytes, buf size must be equal to ({@link IntPacker#NUM_OF_INTS} * {@link |
| * IntPacker#width} / 8) |
| */ |
| public void pack8Values(int[] values, int offset, byte[] buf) { |
| int bufIdx = 0; |
| int valueIdx = offset; |
| // remaining bits for the current unfinished Integer |
| int leftBit = 0; |
| |
| while (valueIdx < NUM_OF_INTS + offset) { |
| // buffer is used for saving 32 bits as a part of result |
| int buffer = 0; |
| // remaining size of bits in the 'buffer' |
| int leftSize = 32; |
| |
| // encode the left bits of current Integer to 'buffer' |
| if (leftBit > 0) { |
| buffer |= (values[valueIdx] << (32 - leftBit)); |
| leftSize -= leftBit; |
| leftBit = 0; |
| valueIdx++; |
| } |
| |
| while (leftSize >= width && valueIdx < NUM_OF_INTS + offset) { |
| // encode one Integer to the 'buffer' |
| buffer |= (values[valueIdx] << (leftSize - width)); |
| leftSize -= width; |
| valueIdx++; |
| } |
| // If the remaining space of the buffer can not save the bits for one Integer, |
| if (leftSize > 0 && valueIdx < NUM_OF_INTS + offset) { |
| // put the first 'leftSize' bits of the Integer into remaining space of the |
| // buffer |
| buffer |= (values[valueIdx] >>> (width - leftSize)); |
| leftBit = width - leftSize; |
| } |
| |
| // put the buffer into the final result |
| for (int j = 0; j < 4; j++) { |
| buf[bufIdx] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF); |
| bufIdx++; |
| if (bufIdx >= width) { |
| return; |
| } |
| } |
| } |
| } |
| |
| /** |
| * decode Integers from byte array. |
| * |
| * @param buf - array where bytes are in. |
| * @param offset - offset of first byte to be decoded in buf |
| * @param values - decoded result , the length of 'values' should be @{link IntPacker#NUM_OF_INTS} |
| */ |
| public void unpack8Values(byte[] buf, int offset, int[] values) { |
| int byteIdx = offset; |
| long buffer = 0; |
| // total bits which have read from 'buf' to 'buffer'. i.e., |
| // number of available bits to be decoded. |
| int totalBits = 0; |
| int valueIdx = 0; |
| |
| while (valueIdx < NUM_OF_INTS) { |
| // If current available bits are not enough to decode one Integer, |
| // then add next byte from buf to 'buffer' until totalBits >= width |
| while (totalBits < width) { |
| buffer = (buffer << 8) | (buf[byteIdx] & 0xFF); |
| byteIdx++; |
| totalBits += 8; |
| } |
| |
| // If current available bits are enough to decode one Integer, |
| // then decode one Integer one by one until left bits in 'buffer' is |
| // not enough to decode one Integer. |
| while (totalBits >= width && valueIdx < 8) { |
| values[valueIdx] = (int) (buffer >>> (totalBits - width)); |
| valueIdx++; |
| totalBits -= width; |
| buffer = buffer & ((1 << totalBits) - 1); |
| } |
| } |
| } |
| |
| /** |
| * decode all values from 'buf' with specified offset and length decoded result will be saved in |
| * the array named 'values'. |
| * |
| * @param buf array where all bytes are in. |
| * @param length length of bytes to be decoded in buf. |
| * @param values decoded result. |
| */ |
| public void unpackAllValues(byte[] buf, int length, int[] values) { |
| int idx = 0; |
| int k = 0; |
| while (idx < length) { |
| int[] tv = new int[8]; |
| // decode 8 values one time, current result will be saved in the array named 'tv' |
| unpack8Values(buf, idx, tv); |
| System.arraycopy(tv, 0, values, k, 8); |
| idx += width; |
| k += 8; |
| } |
| } |
| |
| public void setWidth(int width) { |
| this.width = width; |
| } |
| } |