/* * 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.util.bkd; import java.io.Closeable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.BytesRef; /** Utility class to write new points into in-heap arrays. * * @lucene.internal */ public final class HeapPointWriter implements PointWriter { public int[] docIDs; public long[] ordsLong; public int[] ords; private int nextWrite; private boolean closed; final int maxSize; public final int valuesPerBlock; final int packedBytesLength; final boolean singleValuePerDoc; // NOTE: can't use ByteBlockPool because we need random-write access when sorting in heap public final List<byte[]> blocks = new ArrayList<>(); public HeapPointWriter(int initSize, int maxSize, int packedBytesLength, boolean longOrds, boolean singleValuePerDoc) { docIDs = new int[initSize]; this.maxSize = maxSize; this.packedBytesLength = packedBytesLength; this.singleValuePerDoc = singleValuePerDoc; if (singleValuePerDoc) { this.ordsLong = null; this.ords = null; } else { if (longOrds) { this.ordsLong = new long[initSize]; } else { this.ords = new int[initSize]; } } // 4K per page, unless each value is > 4K: valuesPerBlock = Math.max(1, 4096/packedBytesLength); } public void copyFrom(HeapPointWriter other) { if (docIDs.length < other.nextWrite) { throw new IllegalStateException("docIDs.length=" + docIDs.length + " other.nextWrite=" + other.nextWrite); } System.arraycopy(other.docIDs, 0, docIDs, 0, other.nextWrite); if (singleValuePerDoc == false) { if (other.ords != null) { assert this.ords != null; System.arraycopy(other.ords, 0, ords, 0, other.nextWrite); } else { assert this.ordsLong != null; System.arraycopy(other.ordsLong, 0, ordsLong, 0, other.nextWrite); } } for(byte[] block : other.blocks) { blocks.add(block.clone()); } nextWrite = other.nextWrite; } public void readPackedValue(int index, byte[] bytes) { assert bytes.length == packedBytesLength; int block = index / valuesPerBlock; int blockIndex = index % valuesPerBlock; System.arraycopy(blocks.get(block), blockIndex * packedBytesLength, bytes, 0, packedBytesLength); } /** Returns a reference, in <code>result</code>, to the byte[] slice holding this value */ public void getPackedValueSlice(int index, BytesRef result) { int block = index / valuesPerBlock; int blockIndex = index % valuesPerBlock; result.bytes = blocks.get(block); result.offset = blockIndex * packedBytesLength; assert result.length == packedBytesLength; } void writePackedValue(int index, byte[] bytes) { assert bytes.length == packedBytesLength; int block = index / valuesPerBlock; int blockIndex = index % valuesPerBlock; //System.out.println("writePackedValue: index=" + index + " bytes.length=" + bytes.length + " block=" + block + " blockIndex=" + blockIndex + " valuesPerBlock=" + valuesPerBlock); while (blocks.size() <= block) { // If this is the last block, only allocate as large as necessary for maxSize: int valuesInBlock = Math.min(valuesPerBlock, maxSize - (blocks.size() * valuesPerBlock)); blocks.add(new byte[valuesInBlock*packedBytesLength]); } System.arraycopy(bytes, 0, blocks.get(block), blockIndex * packedBytesLength, packedBytesLength); } @Override public void append(byte[] packedValue, long ord, int docID) { assert closed == false; assert packedValue.length == packedBytesLength; if (docIDs.length == nextWrite) { int nextSize = Math.min(maxSize, ArrayUtil.oversize(nextWrite+1, Integer.BYTES)); assert nextSize > nextWrite: "nextSize=" + nextSize + " vs nextWrite=" + nextWrite; docIDs = Arrays.copyOf(docIDs, nextSize); if (singleValuePerDoc == false) { if (ordsLong != null) { ordsLong = Arrays.copyOf(ordsLong, nextSize); } else { ords = Arrays.copyOf(ords, nextSize); } } } writePackedValue(nextWrite, packedValue); if (singleValuePerDoc == false) { if (ordsLong != null) { ordsLong[nextWrite] = ord; } else { assert ord <= Integer.MAX_VALUE; ords[nextWrite] = (int) ord; } } docIDs[nextWrite] = docID; nextWrite++; } @Override public PointReader getReader(long start, long length) { assert start + length <= docIDs.length: "start=" + start + " length=" + length + " docIDs.length=" + docIDs.length; assert start + length <= nextWrite: "start=" + start + " length=" + length + " nextWrite=" + nextWrite; return new HeapPointReader(blocks, valuesPerBlock, packedBytesLength, ords, ordsLong, docIDs, (int) start, Math.toIntExact(start+length), singleValuePerDoc); } @Override public PointReader getSharedReader(long start, long length, List<Closeable> toCloseHeroically) { return new HeapPointReader(blocks, valuesPerBlock, packedBytesLength, ords, ordsLong, docIDs, (int) start, nextWrite, singleValuePerDoc); } @Override public void close() { closed = true; } @Override public void destroy() { } @Override public String toString() { return "HeapPointWriter(count=" + nextWrite + " alloc=" + docIDs.length + ")"; } }