/* * 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.packed; import static org.apache.lucene.util.packed.PackedInts.checkBlockSize; import static org.apache.lucene.util.packed.PackedInts.numBlocks; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.LongValues; import org.apache.lucene.util.RamUsageEstimator; /** * Base implementation for {@link PagedMutable} and {@link PagedGrowableWriter}. * @lucene.internal */ abstract class AbstractPagedMutable<T extends AbstractPagedMutable<T>> extends LongValues implements Accountable { static final int MIN_BLOCK_SIZE = 1 << 6; static final int MAX_BLOCK_SIZE = 1 << 30; final long size; final int pageShift; final int pageMask; final PackedInts.Mutable[] subMutables; final int bitsPerValue; AbstractPagedMutable(int bitsPerValue, long size, int pageSize) { this.bitsPerValue = bitsPerValue; this.size = size; pageShift = checkBlockSize(pageSize, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); pageMask = pageSize - 1; final int numPages = numBlocks(size, pageSize); subMutables = new PackedInts.Mutable[numPages]; } protected final void fillPages() { final int numPages = numBlocks(size, pageSize()); for (int i = 0; i < numPages; ++i) { // do not allocate for more entries than necessary on the last page final int valueCount = i == numPages - 1 ? lastPageSize(size) : pageSize(); subMutables[i] = newMutable(valueCount, bitsPerValue); } } protected abstract PackedInts.Mutable newMutable(int valueCount, int bitsPerValue); final int lastPageSize(long size) { final int sz = indexInPage(size); return sz == 0 ? pageSize() : sz; } final int pageSize() { return pageMask + 1; } /** The number of values. */ public final long size() { return size; } final int pageIndex(long index) { return (int) (index >>> pageShift); } final int indexInPage(long index) { return (int) index & pageMask; } @Override public final long get(long index) { assert index >= 0 && index < size; final int pageIndex = pageIndex(index); final int indexInPage = indexInPage(index); return subMutables[pageIndex].get(indexInPage); } /** Set value at <code>index</code>. */ public final void set(long index, long value) { assert index >= 0 && index < size; final int pageIndex = pageIndex(index); final int indexInPage = indexInPage(index); subMutables[pageIndex].set(indexInPage, value); } protected long baseRamBytesUsed() { return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + RamUsageEstimator.NUM_BYTES_OBJECT_REF + Long.BYTES + 3 * Integer.BYTES; } @Override public long ramBytesUsed() { long bytesUsed = RamUsageEstimator.alignObjectSize(baseRamBytesUsed()); bytesUsed += RamUsageEstimator.alignObjectSize(RamUsageEstimator.shallowSizeOf(subMutables)); for (PackedInts.Mutable gw : subMutables) { bytesUsed += gw.ramBytesUsed(); } return bytesUsed; } protected abstract T newUnfilledCopy(long newSize); /** Create a new copy of size <code>newSize</code> based on the content of * this buffer. This method is much more efficient than creating a new * instance and copying values one by one. */ public final T resize(long newSize) { final T copy = newUnfilledCopy(newSize); final int numCommonPages = Math.min(copy.subMutables.length, subMutables.length); final long[] copyBuffer = new long[1024]; for (int i = 0; i < copy.subMutables.length; ++i) { final int valueCount = i == copy.subMutables.length - 1 ? lastPageSize(newSize) : pageSize(); final int bpv = i < numCommonPages ? subMutables[i].getBitsPerValue() : this.bitsPerValue; copy.subMutables[i] = newMutable(valueCount, bpv); if (i < numCommonPages) { final int copyLength = Math.min(valueCount, subMutables[i].size()); PackedInts.copy(subMutables[i], 0, copy.subMutables[i], 0, copyLength, copyBuffer); } } return copy; } /** Similar to {@link ArrayUtil#grow(long[], int)}. */ public final T grow(long minSize) { assert minSize >= 0; if (minSize <= size()) { @SuppressWarnings("unchecked") final T result = (T) this; return result; } long extra = minSize >>> 3; if (extra < 3) { extra = 3; } final long newSize = minSize + extra; return resize(newSize); } /** Similar to {@link ArrayUtil#grow(long[])}. */ public final T grow() { return grow(size() + 1); } @Override public final String toString() { return getClass().getSimpleName() + "(size=" + size() + ",pageSize=" + pageSize() + ")"; } }