/* * 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.lucene.index; import java.util.ArrayList; import java.util.List; import org.apache.lucene.util.Counter; import org.apache.lucene.util.IntBlockPool; import org.apache.lucene.util.LuceneTestCase; /** * tests basic {@link IntBlockPool} functionality */ public class TestIntBlockPool extends LuceneTestCase { public void testSingleWriterReader() { Counter bytesUsed = Counter.newCounter(); IntBlockPool pool = new IntBlockPool(new ByteTrackingAllocator(bytesUsed)); for (int j = 0; j < 2; j++) { IntBlockPool.SliceWriter writer = new IntBlockPool.SliceWriter(pool); int start = writer.startNewSlice(); int num = atLeast(100); for (int i = 0; i < num; i++) { writer.writeInt(i); } int upto = writer.getCurrentOffset(); IntBlockPool.SliceReader reader = new IntBlockPool.SliceReader(pool); reader.reset(start, upto); for (int i = 0; i < num; i++) { assertEquals(i, reader.readInt()); } assertTrue(reader.endOfSlice()); if (random().nextBoolean()) { pool.reset(true, false); assertEquals(0, bytesUsed.get()); } else { pool.reset(true, true); assertEquals(IntBlockPool.INT_BLOCK_SIZE * Integer.BYTES, bytesUsed.get()); } } } public void testMultipleWriterReader() { Counter bytesUsed = Counter.newCounter(); IntBlockPool pool = new IntBlockPool(new ByteTrackingAllocator(bytesUsed)); for (int j = 0; j < 2; j++) { List<StartEndAndValues> holders = new ArrayList<>(); int num = atLeast(4); for (int i = 0; i < num; i++) { holders.add(new StartEndAndValues(random().nextInt(1000))); } IntBlockPool.SliceWriter writer = new IntBlockPool.SliceWriter(pool); IntBlockPool.SliceReader reader = new IntBlockPool.SliceReader(pool); int numValuesToWrite = atLeast(10000); for (int i = 0; i < numValuesToWrite; i++) { StartEndAndValues values = holders .get(random().nextInt(holders.size())); if (values.valueCount == 0) { values.start = writer.startNewSlice(); } else { writer.reset(values.end); } writer.writeInt(values.nextValue()); values.end = writer.getCurrentOffset(); if (random().nextInt(5) == 0) { // pick one and reader the ints assertReader(reader, holders.get(random().nextInt(holders.size()))); } } while (!holders.isEmpty()) { StartEndAndValues values = holders.remove(random().nextInt( holders.size())); assertReader(reader, values); } if (random().nextBoolean()) { pool.reset(true, false); assertEquals(0, bytesUsed.get()); } else { pool.reset(true, true); assertEquals(IntBlockPool.INT_BLOCK_SIZE * Integer.BYTES, bytesUsed.get()); } } } private static class ByteTrackingAllocator extends IntBlockPool.Allocator { private final Counter bytesUsed; public ByteTrackingAllocator(Counter bytesUsed) { this(IntBlockPool.INT_BLOCK_SIZE, bytesUsed); } public ByteTrackingAllocator(int blockSize, Counter bytesUsed) { super(blockSize); this.bytesUsed = bytesUsed; } @Override public int[] getIntBlock() { bytesUsed.addAndGet(blockSize * Integer.BYTES); return new int[blockSize]; } @Override public void recycleIntBlocks(int[][] blocks, int start, int end) { bytesUsed .addAndGet(-((end - start) * blockSize * Integer.BYTES)); } } private void assertReader(IntBlockPool.SliceReader reader, StartEndAndValues values) { reader.reset(values.start, values.end); for (int i = 0; i < values.valueCount; i++) { assertEquals(values.valueOffset + i, reader.readInt()); } assertTrue(reader.endOfSlice()); } private static class StartEndAndValues { int valueOffset; int valueCount; int start; int end; public StartEndAndValues(int valueOffset) { this.valueOffset = valueOffset; } public int nextValue() { return valueOffset + valueCount++; } } }