/** * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com) * * Licensed 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 com.linkedin.pinot.core.indexsegment.utils; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Random; import java.util.SortedSet; import java.util.TreeSet; import org.testng.Assert; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; /** * Tests for SearchableByteBufferUtil * */ public class SearchableByteBufferUtilTest { private static final int DISTINCT_VALUES = 10000; private short[] _shorts = new short[DISTINCT_VALUES]; private int[] _ints = new int[DISTINCT_VALUES]; private long[] _longs = new long[DISTINCT_VALUES]; private float[] _floats = new float[DISTINCT_VALUES]; private double[] _doubles = new double[DISTINCT_VALUES]; private SearchableByteBufferUtil _searchableByteBufferUtil; private GenericRowColumnDataFileReader _genericRowColumnDataFileReader; @BeforeSuite public void setup() throws Exception { // Generate DISTINCT_VALUES shorts, ints, longs, floats and doubles Random random = new Random(); SortedSet<Short> shortValues = new TreeSet<Short>(); SortedSet<Integer> intValues = new TreeSet<Integer>(); SortedSet<Long> longValues = new TreeSet<Long>(); SortedSet<Float> floatValues = new TreeSet<Float>(); SortedSet<Double> doubleValues = new TreeSet<Double>(); // Add boundary conditions shortValues.add(Short.MIN_VALUE); shortValues.add(Short.MAX_VALUE); intValues.add(Integer.MIN_VALUE); intValues.add(Integer.MAX_VALUE); longValues.add(Long.MIN_VALUE); longValues.add(Long.MAX_VALUE); floatValues.add(Float.MIN_VALUE); floatValues.add(Float.MAX_VALUE); // floatValues.add(Float.NaN); floatValues.add(Float.NEGATIVE_INFINITY); floatValues.add(Float.POSITIVE_INFINITY); doubleValues.add(Double.MIN_VALUE); doubleValues.add(Double.MAX_VALUE); // doubleValues.add(Double.NaN); doubleValues.add(Double.NEGATIVE_INFINITY); doubleValues.add(Double.POSITIVE_INFINITY); while (shortValues.size() < DISTINCT_VALUES) { shortValues.add((short) random.nextInt()); } while (intValues.size() < DISTINCT_VALUES) { intValues.add(random.nextInt()); } while (longValues.size() < DISTINCT_VALUES) { longValues.add(random.nextLong()); } while(floatValues.size() < DISTINCT_VALUES) { floatValues.add(random.nextFloat()); } while(doubleValues.size() < DISTINCT_VALUES) { doubleValues.add(random.nextDouble()); } // Copy these values into arrays int i = 0; for (Short shortValue : shortValues) { _shorts[i] = shortValue; ++i; } i = 0; for (Integer intValue : intValues) { _ints[i] = intValue; ++i; } i = 0; for (Long longValue : longValues) { _longs[i] = longValue; ++i; } i = 0; for (Float floatValue : floatValues) { _floats[i] = floatValue; ++i; } i = 0; for (Double doubleValue : doubleValues) { _doubles[i] = doubleValue; ++i; } // Write a file that multiplexes all these values final int SHORTS_OFFSET = 0; final int INTS_OFFSET = SHORTS_OFFSET + Short.SIZE / Byte.SIZE; final int LONGS_OFFSET = INTS_OFFSET + Integer.SIZE / Byte.SIZE; final int FLOATS_OFFSET = LONGS_OFFSET + Long.SIZE / Byte.SIZE; final int DOUBLES_OFFSET = FLOATS_OFFSET + Float.SIZE / Byte.SIZE; final int ROW_WIDTH = DOUBLES_OFFSET + Double.SIZE / Byte.SIZE; ByteBuffer buffer = ByteBuffer.allocate(ROW_WIDTH * DISTINCT_VALUES); for(i = 0; i < DISTINCT_VALUES; ++i) { final int baseOffset = i * ROW_WIDTH; buffer.putShort(baseOffset + SHORTS_OFFSET, _shorts[i]); buffer.putInt(baseOffset + INTS_OFFSET, _ints[i]); buffer.putLong(baseOffset + LONGS_OFFSET, _longs[i]); buffer.putFloat(baseOffset + FLOATS_OFFSET, _floats[i]); buffer.putDouble(baseOffset + DOUBLES_OFFSET, _doubles[i]); } File tempFile = File.createTempFile("pinot-test", ".tmp"); tempFile.deleteOnExit(); FileChannel channel = new FileOutputStream(tempFile).getChannel(); // buffer.flip(); channel.write(buffer); channel.close(); _genericRowColumnDataFileReader = GenericRowColumnDataFileReader.forHeap(tempFile, DISTINCT_VALUES, 5, new int[] { Short.SIZE / Byte.SIZE, Integer.SIZE / Byte.SIZE, Long.SIZE / Byte.SIZE, Float.SIZE / Byte.SIZE, Double.SIZE / Byte.SIZE }); _searchableByteBufferUtil = new SearchableByteBufferUtil(_genericRowColumnDataFileReader); } @Test public void testBinarySearch() { // Iterate through the sorted arrays, checking that their position matches with what we have for(int i = 0; i < DISTINCT_VALUES; ++i) { Assert.assertEquals(_searchableByteBufferUtil.binarySearch(0, _shorts[i]), i); Assert.assertEquals(_searchableByteBufferUtil.binarySearch(1, _ints[i]), i); Assert.assertEquals(_searchableByteBufferUtil.binarySearch(2, _longs[i]), i); Assert.assertEquals(_searchableByteBufferUtil.binarySearch(3, _floats[i]), i); Assert.assertEquals(_searchableByteBufferUtil.binarySearch(4, _doubles[i]), i); } } @AfterSuite public void destroy() { _genericRowColumnDataFileReader.close(); } }