blob: a4d97f494ce7712e78bf63669faaad8aab48386c [file] [log] [blame]
/*
* 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 java.io.IOException;
import org.apache.lucene.store.DataOutput;
import org.apache.lucene.util.RamUsageEstimator;
/**
* Implements {@link PackedInts.Mutable}, but grows the
* bit count of the underlying packed ints on-demand.
* <p>Beware that this class will accept to set negative values but in order
* to do this, it will grow the number of bits per value to 64.
*
* <p>@lucene.internal</p>
*/
public class GrowableWriter extends PackedInts.Mutable {
private long currentMask;
private PackedInts.Mutable current;
private final float acceptableOverheadRatio;
/**
* @param startBitsPerValue the initial number of bits per value, may grow depending on the data
* @param valueCount the number of values
* @param acceptableOverheadRatio an acceptable overhead ratio
*/
public GrowableWriter(int startBitsPerValue, int valueCount, float acceptableOverheadRatio) {
this.acceptableOverheadRatio = acceptableOverheadRatio;
current = PackedInts.getMutable(valueCount, startBitsPerValue, this.acceptableOverheadRatio);
currentMask = mask(current.getBitsPerValue());
}
private static long mask(int bitsPerValue) {
return bitsPerValue == 64 ? ~0L : PackedInts.maxValue(bitsPerValue);
}
@Override
public long get(int index) {
return current.get(index);
}
@Override
public int size() {
return current.size();
}
@Override
public int getBitsPerValue() {
return current.getBitsPerValue();
}
public PackedInts.Mutable getMutable() {
return current;
}
private void ensureCapacity(long value) {
if ((value & currentMask) == value) {
return;
}
final int bitsRequired = PackedInts.unsignedBitsRequired(value);
assert bitsRequired > current.getBitsPerValue();
final int valueCount = size();
PackedInts.Mutable next = PackedInts.getMutable(valueCount, bitsRequired, acceptableOverheadRatio);
PackedInts.copy(current, 0, next, 0, valueCount, PackedInts.DEFAULT_BUFFER_SIZE);
current = next;
currentMask = mask(current.getBitsPerValue());
}
@Override
public void set(int index, long value) {
ensureCapacity(value);
current.set(index, value);
}
@Override
public void clear() {
current.clear();
}
public GrowableWriter resize(int newSize) {
GrowableWriter next = new GrowableWriter(getBitsPerValue(), newSize, acceptableOverheadRatio);
final int limit = Math.min(size(), newSize);
PackedInts.copy(current, 0, next, 0, limit, PackedInts.DEFAULT_BUFFER_SIZE);
return next;
}
@Override
public int get(int index, long[] arr, int off, int len) {
return current.get(index, arr, off, len);
}
@Override
public int set(int index, long[] arr, int off, int len) {
long max = 0;
for (int i = off, end = off + len; i < end; ++i) {
// bitwise or is nice because either all values are positive and the
// or-ed result will require as many bits per value as the max of the
// values, or one of them is negative and the result will be negative,
// forcing GrowableWriter to use 64 bits per value
max |= arr[i];
}
ensureCapacity(max);
return current.set(index, arr, off, len);
}
@Override
public void fill(int fromIndex, int toIndex, long val) {
ensureCapacity(val);
current.fill(fromIndex, toIndex, val);
}
@Override
public long ramBytesUsed() {
return RamUsageEstimator.alignObjectSize(
RamUsageEstimator.NUM_BYTES_OBJECT_HEADER
+ RamUsageEstimator.NUM_BYTES_OBJECT_REF
+ Long.BYTES
+ Float.BYTES)
+ current.ramBytesUsed();
}
@Override
public void save(DataOutput out) throws IOException {
current.save(out);
}
}